我在Internet上的任何地方都找不到它,所以想把它添加为文档。
我想在非显示字符\30
(“ RecordSeparator”)周围加入一个json数组,以便可以安全地在bash中对其进行迭代,但是我不知道该怎么做。我尝试了echo '["one","two","three"]' | jq 'join("\30")'
和它的一些排列方式,但是没有用。
结果证明解决方案非常简单。...(请参阅答案)
答案 0 :(得分:2)
使用jq -j
消除记录之间的文字换行符,并仅使用您自己的定界符。这适用于您的简单情况:
#!/usr/bin/env bash
data='["one","two","three"]'
sep=$'\x1e' # works only for non-NUL characters, see NUL version below
while IFS= read -r -d "$sep" rec || [[ $rec ]]; do
printf 'Record: %q\n' "$rec"
done < <(jq -j --arg sep "$sep" 'join($sep)' <<<"$data")
...但是它也可以用于更幼稚的情况,即幼稚的答案会失败:
#!/usr/bin/env bash
data='["two\nlines","*"]'
while IFS= read -r -d $'\x1e' rec || [[ $rec ]]; do
printf 'Record: %q\n' "$rec"
done < <(jq -j 'join("\u001e")' <<<"$data")
返回(在Cygwin上运行时,因此在CRLF上运行):
Record: $'two\r\nlines'
Record: \*
也就是说,如果我很生气地使用它,我建议使用NUL分隔符,并从输入值中过滤掉它们:
#!/usr/bin/env bash
data='["two\nlines","three\ttab-separated\twords","*","nul\u0000here"]'
while IFS= read -r -d '' rec || [[ $rec ]]; do
printf 'Record: %q\n' "$rec"
done < <(jq -j '[.[] | gsub("\u0000"; "@NUL@")] | join("\u0000")' <<<"$data")
NUL是一个不错的选择,因为它是一个字符,它根本不能存储在C字符串中(就像bash所使用的那样),因此在可以存储的数据范围内没有任何损失。当它们被切除时会忠实地传达-如果它们 did 进入外壳,它将(取决于版本)丢弃它们,或在第一次出现时截断字符串。< / p>
答案 1 :(得分:1)
解决问题的推荐方法是使用-c命令行 选项,例如如下:
echo "$data" | jq -c '.[]' |
while read -r rec
do
echo "Record: $rec"
done
输出:
Record: "one"
Record: "two"
Record: "three"
基于$'\30'
的OP的答案中的提案存在几个问题
首先,它无法可靠运行,例如在Mac上使用bash
输出为:Record: "one\u0018two\u0018three"
;
这是因为jq正确地将八进制30转换为\u0018
JSON字符串中。
第二,RS是ASCII十进制30,即八进制36,其中
将在外壳中写为$'\36'
。
如果改用此值,程序将产生:
Record: "one\u001etwo\u001ethree"
,因为那是
带有嵌入式RS字符的正确JSON字符串。 (因为记录$'\30'
是Control-X。)
第三点,正如查尔斯·达菲(Charles Duffy)所指出的,“因为$(...)中的rec本质上是越野车。”
第四,任何假设jq的方法将来都会接受 非法JSON字符串在某种意义上是脆弱的 将来,jq可能会禁止它们或至少需要命令行 切换为允许他们。
第五,不能保证unset IFS
会将IFS预先恢复到其状态。
答案 2 :(得分:0)
与--seq
命令行选项一起使用时,RS字符在jq中是特殊的。例如,使用存储在名为data
的shell变量中的JSON数组,我们可以按以下方式调用jq:
$ jq -n --seq --argjson arg '[1,2]' '$arg | .[]'
这是笔录:
$ data='["one","two","three"]'
$ jq -n --seq --argjson arg "$data" '$arg | .[]' | tr $'\36' X
X"one"
X"two"
X"three"
$
答案 3 :(得分:-1)
您只需使用bash的$'\30'
语法插入特殊字符即可,例如:echo '["one","two","three"]' | jq '. | join("'$'\30''")'
。
这是整个工作示例:
data='["one","two","three"]'
IFS=$'\30'
for rec in $(echo "$data" | jq '. | join("'$'\30''")'); do
echo "Record: $rec"
done
unset IFS
此打印
Record: one
Record: two
Record: three
符合预期。
注意:重要的是,不要在for循环中引用该子shell。如果引用它,则无论RecordSeparator字符如何,它都将被当作一个参数。如果您不引用它,它将按预期工作。