Use 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如果它到达 shell,它会(取决于版本)丢弃它们,或者在第一次出现时截断字符串。