处理相同的标准输入两次并附加输出

处理相同的标准输入两次并附加输出

我有一个 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//如果行首有 则给出错误,因为这不是有效的 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

但是有一个问题!

我必须用一个命令来完成此操作。这是因为,我是通过 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选定的文本作为标准输入,对其进行处理,然后输出处理后的文本。

因此,据我了解,我必须读取标准输入两次(一次使用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)')} 

似乎也提供了与 Update 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

现在,关于你的“我必须用一个命令来完成此操作“。请记住,没有什么可以阻止您创建自己的命令(程序、脚本)。将必要的命令集放入一个文件中,使该文件可执行,然后将其放入您的 . 中的目录中$PATH。我一直使用$HOME/bin并且我export PATH="$PATH:$HOME/bin"在 my~/.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

由于我做了一些研究,所以也想回答我的问题。对于单行命令:

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"

相关内容