sed 参数替换为多行引号字符串

sed 参数替换为多行引号字符串

目标

将文本替换"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",。或者,您可以使用ased 命令附加文本:

$ 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"
  }
}

相关内容