同じ標準入力を2回処理し、出力を追加します。

同じ標準入力を2回処理し、出力を追加します。

次のjsonファイルがあります。

[
    {
        "key": "alt+down",
        "command": "-editor.action.moveLinesDownAction",
        "when": "editorTextFocus && !editorReadonly"
    },
    {
        "key": "alt+f12",
        "command": "editor.action.peekDefinition",
        "when": "editorHasDefinitionProvider && editorTextFocus && !inReferenceSearchEditor && !isInEmbeddedEditor"
    }
]
//  {
//      "key": "ctrl+shift+d",
//      "command": "workbench.action.toggleMaximizedPanel"
//  },
//  {
//      "key": "ctrl+shift+d",
//      "command": "-workbench.view.debug",
//      "when": "viewContainer.workbench.view.debug.enabled"
//  }

このファイルをソートしたいです。

jq//行の先頭に1つがあると、有効なjsonではないため、エラーが発生します。

したがって、このファイルを並べ替えるために私が思いついたコマンドは次のようになります。

grep -v '^[ ]*//' keybindings.json | jq 'sort_by(.key)'

しかし、私はコメント付きの行を捨てたくありません。したがって、コメントアウトされた行を取得するために私が思いついたコマンドは次のようになります。

grep '^[ ]*//' keybindings.json

今私の問題を解決するために簡単にできることは次のとおりです。

#!/bin/bash

SORTED_JSON=$(grep -v '^[ ]*//' keybindings.json | jq 'sort_by(.key)')
COMMENTED_JSON=$(grep '^[ ]*//' keybindings.json)

echo "$SORTED_JSON" >keybindings.json
echo "$COMMENTED_JSON" >>keybindings.json

しかし、問題があります!

これは1つのコマンドで行う必要があります。これは、vscode設定でこれを行うためです。

"filterText.commandList": [
    {
        "name": "Sort VSCode Keybindings",
        "description": "Sorts keybindings.json by keys. Select everything except the comment in fist line. Then run this command",
        "command": "jq 'sort_by(.key)'"
    }
]

選択したテキストを標準入力にインポートしてcommand処理し、処理されたテキストを出力します。

だから私が理解したところによると、stdinを2回読む必要があります(一度grep -v '^[ ]*//' | jq 'sort_by(.key)'、もう一度grep '^[ ]*//')。両方のコマンドの出力をstdoutに追加します。

この問題をどのように解決できますか?

アップデート1:

私はそれらすべてを試しました

cat keybindings.json| {grep -v '^[ ]*//' | jq 'sort_by(.key)' ; grep '^[ ]*//'}

そして

cat keybindings.json| (grep -v '^[ ]*//' | jq 'sort_by(.key)' ; grep '^[ ]*//')

コメント行は表示されません。

アップデート2:

次は私が期待していたものに近いようです。ただし、ここでは、コメントアウトされた行はコメントアウトされていない行の前にあります。

$ cat keybindings.json| tee >(grep -v '^[ ]*//' | jq 'sort_by(.key)') >(grep '^[ ]*//') > /dev/null 2>&1
    //  {
    //      "key": "ctrl+shift+d",
    //      "command": "workbench.action.toggleMaximizedPanel"
    //  },
    //  {
    //      "key": "ctrl+shift+d",
    //      "command": "-workbench.view.debug",
    //      "when": "viewContainer.workbench.view.debug.enabled"
    //  }
[
  {
    "key": "alt+down",
    "command": "-editor.action.moveLinesDownAction",
    "when": "editorTextFocus && !editorReadonly"
  },
  {
    "key": "alt+f12",
    "command": "editor.action.peekDefinition",
    "when": "editorHasDefinitionProvider && editorTextFocus && !inReferenceSearchEditor && !isInEmbeddedEditor"
  }
]

アップデート3:

cat keybindings.json| (tee >(grep '^[ ]*//'); tee >(grep -v '^[ ]*//' | jq 'sort_by(.key)'))

または、

cat keybindings.json| {tee >(grep '^[ ]*//'); tee >(grep -v '^[ ]*//' | jq 'sort_by(.key)')} 

また、更新3と同じ出力を提供するようです(コメントなしの行の前にコメントされた行)。

答え1

コメント行とコメント以外の行を混合して挿入する方法はありません。これを別々のブロックとして処理し、別々に処理する必要があります。

コメント行を最初に出力しても問題ない場合は、awk次のように使用できます。

awk '{ if ($0 ~ /^ *\/\//) { print } else { print | "jq \"sort_by(.key)\"" } }' keybindings.json

ただし、コメント行が最後に表示されるようにしたいので、コメント行を保存して後で出力する必要があります。

awk '
    # Define a convenience variable for the jq process
    BEGIN {
        jq = "jq \"sort_by(.key)\""
    }

    # Each line hits this. Either we save the comment or we feed it to jq
    {
        if ($0 ~ /^ *\/\//) { c[++i] = $0 }
        else { print | jq }
    }
    
    # Close the pipe to get its output and then print the saved comment lines
    END {
        close (jq);
        for (i in c) { print c[i] }
    }
' keybindings.json

今あなたの "1つのコマンドでこれを行う必要があります。自分自身のコマンド(プログラム、スクリプト)を生成するのを妨げるものは何もないことを覚えておいてください$PATH$HOME/binexport PATH="$PATH:$HOME/bin"~/.bash_profile~/.profile

答え2

コメントを文字列に変換し、配列を並べ替え、コメント文字列をコメントに戻します。

コメントを文字列に変換する操作は次のとおりです。

  1. //(オプションのインデントとスペースを含む)で始まる行を検出します。
  2. "この行のすべての項目\"
  3. この行を二重引用符で囲みます。
sed '\:^ *//: { s/"/\\"/g; s/.*/"&"/; }'

次に配列を並べ替えます。

jq 'if type == "array" then sort_by(.key) else . end'

最後に、コメント文字列をコメントに戻します。

  1. "//(および間にオプションのスペースを含む)で始まり、次に終わる行を検出します。"//"
  2. この行の最初の行""最後の行を削除してください。
  3. それぞれ\"をに交換してください"
sed '\:^"\( *//.*\)"$: { s//\1/; s/\\"/"/g; }'

標準入力から完全に読み取られるパイプ:

sed '\:^ *//: { s/"/\\"/g; s/.*/"&"/; }' |
jq 'if type == "array" then sort_by(.key) else . end' |
sed '\:^"\( *//.*\)"$: { s//\1/; s/\\"/"/g; }'

これは、データにエスケープされた二重引用符がまだ含まれていないと仮定します。

答え3

ちょっと勉強したので質問にも答えたかったです。 1行コマンドの場合:

cat keybindings.json | (tee /tmp/program-code-binding.json | grep -v '^[ ]*//' | jq 'sort_by(.key)'; cat /tmp/program-code-binding.json | grep '^[ ]*//')

スクリプトを使用するには、次のようにします。

#!/bin/bash

THESTDIN=$(cat)

SORTED_JSON=$(echo "$THESTDIN" | grep -v '^[ ]*//' | jq 'sort_by(.key)')
COMMENTED_JSON=$(echo "$THESTDIN" | grep '^[ ]*//')

echo "$SORTED_JSON"
echo "$COMMENTED_JSON"

このスクリプトには極端なケースがあります。THESTDIN=$(cat)パイプに何もない場合は、無期限にぶら下がります。。この問題を解決するために、スクリプトは実際には次のようになります。

#!/bin/bash

__=""
THESTDIN=""

read -N1 -t1 __  && {
    (( $? <= 128 ))  && {
        IFS= read -rd '' _stdin
        THESTDIN="$__$_stdin"
    }
}

SORTED_JSON=$(echo "$THESTDIN" | grep -v '^[ ]*//' | jq 'sort_by(.key)')
COMMENTED_JSON=$(echo "$THESTDIN" | grep '^[ ]*//')

echo "$SORTED_JSON"
echo "$COMMENTED_JSON"

関連情報