我需要用 Bash 脚本编写一个 JSON 文件,我知道可以通过像 echo 'something' >> $file
这样的方式逐步构建文件,但是使用 echo 重定向而不是真正的文件输出似乎有点“hacky”。如果这是最好的方法,而且根本不是 hacky 的方法,我很乐意使用 echo
,但我只是想知道是否有更好的方法来从 Bash 脚本中输出文件。
echo
是一个内建命令,而非外部命令,因此它并不像你想象的那么低效。真正低效的是在每个 echo
结尾加上 >> 文件名
。
这样做很糟糕:
# EVIL!
echo "something" >file
echo "first line" >>file
echo "second line" >>file
这很好:
# NOT EVIL!
{
echo "something" >&3
printf '%s\n' "first line" "$second line" >&3
# ... etc ...
} 3>file
只需打开一次输出文件,就可以消除主要的低效率。
明确一点:调用echo
20次比调用cat
一次要更有效率,因为cat
是一个外部进程,不是shell的一部分。 关于运行echo "foo" >>file
20次的高度低效在于需要打开和关闭20次输出文件;而不是echo
本身。
不要使用cat
、echo
、printf
或类似的工具。相反,使用能够理解JSON的工具——任何其他方法都可能导致潜在的不正确(甚至是可利用注入攻击)的结果。
例如:
jq \
--arg something "$some_value_here" \
--arg another "$another_value" \
'.["something"]=$something | .["another_value"]=$another' \
<template.json >output.json
将基于template.json
,生成一个JSON文件,其中something
设置为shell变量"$some_value_here"
的值,而another_value
则设置为第二个值。与天真的方法不同,这将正确处理包含文字引号或其他需要转义才能正确表示的字符的变量值。
尽管上述所有内容都已经说过了--应该避免使用echo
,而应该使用printf
(带有适当的静态格式字符串)。根据< A HREF =“http://pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html”rel =“nofollow noreferrer”> POSIX sh标准:
应用程序使用
除非省略了- n(作为第一个参数)和转义序列,否则无法在所有POSIX系统上可移植地使用echo。
假定IFS具有其标准值或未设置,则printf实用程序可以可移植地用于模拟echo实用程序的任何传统行为,如下所示:
[...]
鼓励新应用程序改用printf而不是echo。
原理
由于历史应用程序中广泛使用echo实用程序,因此未将其过时。希望进行提示而不带s或可能期望回显-n的符合应用程序应使用从第九版系统派生的printf实用程序。
如规定的那样,echo以最简单的方式写入其参数。 echo的两个不同的历史版本以致命的不兼容方式变化。
BSD echo检查字符串-n的第一个参数,这会导致其在输出中在最终参数后面遵循的否则将省略的字符。
System V echo不支持任何选项,但允许在其操作数中使用转义序列,如在OPERANDS部分为XSI实现所述。
echo实用程序不支持实用程序语法指南10,因为历史应用程序依赖于echo以回显除BSD版本中的-n选项之外的所有参数。
echo
,那么你不妨使用多行字符串。 ;) - Mr. Llamajq
中进行了一些令人惊讶的高级分析(包括不同输入源之间的连接),它是一种非常强大的语言。 - Charles Duffy您可以使用cat
和here-document格式:
cat <<'EOF' > output.json
{
"key": "value",
"num": 5,
"tags": ["good", "bad"],
"money": "$0"
}
EOF
$0
可以被替换。echo
0.01s vs cat
0.1s),它几乎快了一个数量级。cat
/echo
之外的其他方法。cat
涉及到 fork()/exec() 循环。 - Charles Duffy将数据构建在环境变量中,并输出一次。
var=something
var="$var something else"
var="$var and another thing"
echo "$var" > file
echo "$var"
-- 来避免字符串分割和值的通配符扩展;如果该值包含换行符,则尤其重要,否则 echo $var
会将其丢弃。 - Charles Duffyvar+="something else"$'\n'
(同时演示了换行符的bash语法)。 - Charles Duffyprintf
一次性发出它们可能更有效率 -- 如果需要,可以使用分隔符。例如:arr=( ); arr+=( "first line" "second line" ); arr+=( "third line" ); printf '%s\n' "${arr[@]}"
(如果想要一个大行而不是 \n
,则不需要)-- 这样可以避免 bash 在数组连接方面的低效率。 - Charles Duffyecho
命令为何被弃用的 RATIONALE 和 APPLICATION notes 部分的 http://pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html。 - Charles Duffyecho
,您还可以使用cat
:cat > myfile << EOF
Hello
World
!
EOF
/usr/bin/cat
这样的外部工具几乎不是一种效率优化。 - Charles Duffy您可以使用cat
和"heredocs"来最小化您需要进行的调用次数。
$ cat foo.sh
cat <<'HERE' > output
This
that
the other
indentation is
preserved
as are
blank lines
The end.
HERE
$ sh foo.sh
$ cat output
This
that
the other
indentation is
preserved
as are
blank lines
The end.
jq
才是正确的工具,虽然它关注的是正确性而非效率。如果你问“在 bash 中编写 JSON 文件的最佳方式是什么”,那将是完全不同的问题。 - Charles Duffy