我有一个 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
将注释转换为字符串,对数组进行排序,然后再次将注释字符串转换为注释。
将注释转换为字符串是通过以下方式完成的
- 检测以
//
(带有可选缩进和空格)开头的行。 "
将这些行中现有的任何内容替换为\"
, 和- 双引号该行。
sed '\:^ *//: { s/"/\\"/g; s/.*/"&"/; }'
然后,对数组进行排序:
jq 'if type == "array" then sort_by(.key) else . end'
最后将注释字符串转回注释:
- 检测以(和
"//
之间有可选空格)开头并以 结尾的行。"
//
"
- 删除这些行的第一行
"
和"
末尾的 。 - 将每个替换
\"
为"
.
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"