目标
将文本替换"scripts: {"
为以下字符串
"scripts": {
"watch": "tsc -w",
在一个json
文件中。
尝试
我为源字符串和目标字符串创建了两个变量:
第一次尝试
SRC='"scripts": {'
DST='"scripts": {
"watch": "tsc -w",'
并运行以下命令:
sed "s/$SRC/$DST/" foo.json
这已经失败了。
第二次尝试
这次我对源字符串和目标字符串转义了双引号:
SRC="\"scripts\": {"
DST="\"scripts\": {
\"watch\": \"tsc -w\",
\"dev\": \"nodemon dist/index.js\","
并运行与上面相同的命令,但失败了。
第三次和第四次尝试
我使用以下命令尝试了上面定义的变量:
sed 's/'"$SRC"'/'"$DST"'/' foo.json
这已经失败了。
所有这些尝试都产生了错误
unterminated 's' command
出了什么问题?
答案1
假设您的 JSON 文档类似于
{
"scripts": {
"other-key": "some value"
}
}
...并且您想将一些其他键值对插入到该.scripts
对象中。然后你可以使用jq
这样做:
$ jq '.scripts.watch |= "tsc -w"' file.json
{
"scripts": {
"other-key": "some value",
"watch": "tsc -w"
}
}
或者,
$ jq '.scripts += { watch: "tsc -w" }' file.json
{
"scripts": {
"other-key": "some value",
"watch": "tsc -w"
}
}
这两个都会代替已经存在的.scripts.watch
条目。
请注意,其中键值对的顺序.scripts
并不重要(因为它不是数组)。
如果要保存输出,请将其重定向到新文件。
向同一个对象添加多个键值对:
$ jq '.scripts += { watch: "tsc -w", dev: "nodemon dist/index.js" }' file.json
{
"scripts": {
"other-key": "some value",
"watch": "tsc -w",
"dev": "nodemon dist/index.js"
}
}
结合jo
创建需要添加到对象的 JSON .scripts
:
$ jq --argjson new "$( jo watch='tsc -w' dev='nodemon dist/index.js' )" '.scripts += $new' file.json
{
"scripts": {
"other-key": "some value",
"watch": "tsc -w",
"dev": "nodemon dist/index.js"
}
}
sed
适合解析面向行的文本。JSON 不包含换行符分隔的记录,也不sed
了解 JSON 的引用和字符编码规则等。要正确解析和修改这样的结构化数据集(或 XML、YAML,甚至在某些情况下是 CSV),您应该使用适当的解析器。
作为在本例中使用的一个额外好处jq
,您会得到一些代码:容易地修改以满足您的需求,并且同样可以轻松修改以支持输入数据结构的更改。
答案2
拘萨罗南达是绝对正确的说专用解析器是完成这项工作的正确工具。然而,这也可以很容易地在sed
(至少使用sed
理解的GNU)中完成。\n
尝试替换整个两行模式会使事情变得更加复杂。相反,只需匹配目标字符串并在其后插入替换内容:
"scripts": {
进而:
$ sed '/"scripts": {/s/$/\n "watch": "tsc -w",/' file
"scripts": {
"watch": "tsc -w",
这意味着“如果此行与字符串 匹配"scripts": {
,则将该行末尾替换为换行符 ( \n
) 后跟"watch": "tsc -w",
。或者,您可以使用a
sed 命令附加文本:
$ sed '/"scripts": {/a\ "watch": "tsc -w",' file
"scripts": {
"watch": "tsc -w",
答案3
在这里,我们创建一个 Sed 命令,但在其中添加一个换行符:
sed "s/$SRC/$DST/" foo.json
这是无效的,因为换行符结束了命令。我们需要编写\n
而不是字面的换行符。在某些 shell(Bash、zsh、其他...,但不是普通的 POSIX shell)中,我们可以使用参数替换来做到这一点:
DST=${DST//$'\n'/\\n}
# // replace every
# $'\n' newline
# / with
# \\n \ and n
对于其他替换字符串,我们可能还需要引用/
:
DST=${DST//\//\\/}
另外,\
(首先执行此操作)并且&
在替换文本中具有特殊含义,因此需要替换。
答案4
由于sed
将所有文本视为regex
,因此我们必须在将字符串插入sed
代码之前对其进行转义。另外,我们需要注意输入的字符串是在命令的左侧还是右侧s///
,因为两侧的 BRE 字符列表不同。
s='[[:blank:]]'
srch_str='"scripts": {'
repl_str='"watch": "tsc -w",'
esc_srch_str=$(
printf '%s\n' "$srch_str" |
sed -e 's:[][\/.^$*]:\\&:g'
)
esc_repl_str=$(
printf '%s\n' "$repl_str" |
sed -e 's:[\/&]:\\&:g'
)
sed -e "
/^$s*$esc_srch_str\$/G
s/^\($s*\).*\n/&\1 $esc_repl_str/
" file
输出:
{
"scripts": {
"watch": "tsc -w",
"other-key": "some value"
}
}