Json 如何在jq'中使用非显示字符,如换行符(\n)和制表符(\t);s";加入;功能

Json 如何在jq'中使用非显示字符,如换行符(\n)和制表符(\t);s";加入;功能,json,bash,variables,jq,Json,Bash,Variables,Jq,我在互联网上的任何地方都找不到这个,所以我想我应该把它添加为文档 我想在非显示字符\30(“RecordSeparator”)周围加入一个json数组,这样我就可以在bash中安全地对其进行迭代,但我不太明白如何做。我尝试了echo'[“一”、“二”、“三”].\jq'join(“\30”)和一些类似的排列,但没有成功 事实证明,解决方案非常简单。。。。(参见答案)您只需使用bash的$'\30'语法在行中插入特殊字符,如下所示:echo'[“一”、“二”、“三”]。|jq'加入(“$”\30“

我在互联网上的任何地方都找不到这个,所以我想我应该把它添加为文档

我想在非显示字符
\30
(“RecordSeparator”)周围加入一个json数组,这样我就可以在bash中安全地对其进行迭代,但我不太明白如何做。我尝试了
echo'[“一”、“二”、“三”].\jq'join(“\30”)
和一些类似的排列,但没有成功


事实证明,解决方案非常简单。。。。(参见答案)

您只需使用bash的
$'\30'
语法在行中插入特殊字符,如下所示:
echo'[“一”、“二”、“三”]。|jq'加入(“$”\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字符无关。如果不引用它,它将按预期工作。

解决此问题的建议方法是使用-c命令行 选项,例如,如下所示:

echo "$data" | jq -c '.[]' |
while read -r rec
do
    echo "Record: $rec"
done
$ jq -n --seq --argjson arg '[1,2]' '$arg | .[]'
输出:

Record: "one"
Record: "two"
Record: "three"
OP建议答案的问题 OP基于
$'\30'

首先,它工作不可靠,例如在Mac上使用bash 输出为:
记录:“一\u0018two\u0018three”
; 这是因为jq正确地将八进制30转换为
\u0018
在JSON字符串中

第二,RS是ASCII十进制30,即八进制36 将在shell中写为
$'\36'
。 如果改用此值,程序将生成:
记录:“one\u001etwo\u001ethree”
因为 包含嵌入RS字符的正确JSON字符串。(对于记录
$'\30'
为Control-X。)

第三,正如查尔斯·达菲(Charles Duffy)所指出的,“对于美元的rec来说,(…)本质上是有缺陷的。”

第四,任何假设jq将来会接受的方法 非法JSON字符串是脆弱的,因为 将来,jq可能会禁止它们,或者至少需要一个命令行 切换到允许它们


第五,
unset-IFS
不能保证预先将IFS恢复到其状态。

使用
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")

也就是说,如果在愤怒中使用这个,我建议使用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")
#/usr/bin/env bash
数据=“[“两条线”,“三条线分隔”、“*”、“nul\u0000here”]
而IFS=read-r-d''rec | |[[$rec]];做
printf'记录:%q\n'$rec'

done<当与
--seq
命令行选项一起使用时,RS字符在jq中是特殊的。例如,使用存储在名为
data
的shell变量中的JSON数组,我们可以如下调用jq:

echo "$data" | jq -c '.[]' |
while read -r rec
do
    echo "Record: $rec"
done
$ 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"
$

最初的
|
是不必要的。
对于美元(…)
中的rec来说本质上是有缺陷的。尝试读取只包含
*
的记录--您将看到它被本地文件名列表替换…相关:如果仔细修改,则无需
取消设置IFS
而IFS=$'\x1e'read-r-recs
将更改范围限定为
read
,并且不会更改任何其他命令的
IFS
值。非常有用的信息,谢谢大家。我现在不仅学会了除了文件列表之外,决不使用
for
循环,而且决不在公共场合谈论它;呵呵。您也可以安全地使用
for
对数组进行迭代--“$@”中的x使用
或者“${foo[@]}”中的x使用
都可以。例如,对已经回答了这个问题的两个人来说,很抱歉,我最初的问题措辞很糟糕。为了清晰起见,我现在对其进行了编辑。具体来说,我只是在寻找在
join
函数中使用非显示字符的语法。我的原始答案(使用类似于
jq'join(“$”\30“”)
和@Charles Duffy的答案(使用
\uxxx
这样的语法:
jq'join(“\u001e”)
)工作正常。请注意@Charles Duffy的答案对于迭代有一些附加值。啊,明白了。基本上,规则是相信您的代码将与未来版本的
jq
一起工作(正如peak所指出的,将来可能会对未替换的文本不那么宽容),您应该使用JSON转义语法。顺便说一句,要将bash转义转换为JSON转义,您可以这样做:
jq-n--arg str$'\030'$str'
,它输出
“\u0018”
。或者您可以使用相同的(
--arg arg
)语法传入文本,并在
jq
中使用
$str code>)警告:此解决方案假定$data中的JSON尚未包含编码的RS字符,或者此类字符应与添加的RS字符一样作为分隔符处理。当然,这是请求本身固有的。(我有时在生产代码中采用这种方法,但当我这样做时,我使用NUL作为分隔符,并显式地从
jq
中的值中过滤掉它们)。@peak,…我已经对其进行了修改,以显示使用NUL分隔符的示例。@CharlesDuffy,您介意解释一下为什么需要
|[rec]]
吗?也可能是为什么需要在读取之前设置
IFS=
read
-d
标志是否会使
IFS
变得不相关?@kael,这是因为
join()
不会在最后一项之后放置尾随项,
read
返回非零退出状态,除非存在结束分隔符,即使在这种情况下它仍然填充目标变量。因此,如果没有该条件,您将丢失列表中的最后一项。