【问题标题】:Bash: source file executes code inside stringsBash:源文件在字符串中执行代码
【发布时间】:2021-10-08 14:00:38
【问题描述】:

(前提:)

嗨,我正在尝试读取一个 JSON 文件,其内容必须复制到我稍后需要的文件中的一个变量中,根据谷歌的说法,唯一的方法是使用 source .我需要避免在采购期间执行命令,而且,作为一个 JSON 文件,所有这些引号都让我有点头疼。

因此,我需要确保它们中的每一个都被转义以被视为纯文本。我尝试使用这样的sed 命令:

sed -e "s/'/\\\'/g" -e 's/"/\\"/g'

再次检查文件,我可以看到每个单引号都被转义了,除了外部引号

ex: {{"foo":"bar"},{"bar":"foo}} -> VAR='{{\"foo\":\"bar\"}{\"bar\":\"foo\"}}' 

不知何故,当我执行采购时,我收到很多关于不存在的命令和目录的错误。

(问题:)

你知道发生了什么吗?这甚至是实现我目标的可行解决方案吗?有没有更好的方法?如果我问的是不可能的,有没有其他方法可以将文件用作字符串变量存储?

(目标一般期望行为:)

  • 读取json
  • 创建配置文件
  • 将 json 内容附加到变量声明字符串 ("VAR=$(readthejson)")
  • 将变量声明字符串附加到conf文件中
  • 源配置文件
  • 使用 var 作为字符串

(试验特定的期望行为:)

a=$( sed -e "s/'/\\\'/g" -e 's/"/\\"/g' myjson.json )
echo "LOCK='$a'"  >> file 

上面的行成功地用 json 内容填充了我的 file 并转义了所有引号 (来自 package-lock.json 文件的示例:)

  LOCK='{
  \"name\": \"Undecided\",
  \"version\": \"0.0.1\",
  \"lockfileVersion\": 2,
  \"requires\": true,
  \"packages\": {
    \"\": {
      \"version\": \"0.0.1\",
      \"dependencies\": {
        \"@capacitor/android\": \"3.1.2\",
        \"@capacitor/app\": \"1.0.2\",
        \"@capacitor/core\": \"3.1.2\",
        \"@capacitor/haptics\": \"1.0.2\",
  ...

此时我希望采购file 会导致我的字符串被加载到我的脚本中并且可以像这样使用:

source file
echo "$LOCK"

输出:

{
  "name": "Undecided",
  "version": "0.0.1",
  "lockfileVersion": 2,
  "requires": true,
  "packages": {
    "": {
      "version": "0.0.1",
      "dependencies": {
        "@capacitor/android": "3.1.2",
        "@capacitor/app": "1.0.2",
        "@capacitor/core": "3.1.2",
        "@capacitor/haptics": "1.0.2",

(实际行为:)

脚本会根据需要转义所有内容。虽然当我获取它时它会输出:

usage: install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]
               [-o owner] file1 file2
       install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]
               [-o owner] file1 ... fileN directory
       install -d [-v] [-g group] [-m mode] [-o owner] directory ...
file: line 2128: },: command not found
file: line 2129: "node_modules/@hapi/bourne":: No such file or directory
file: line 2130: "version":: command not found
file: line 2131: "resolved":: command not found
file: line 2132: "integrity":: command not found
file: line 2133: "deprecated":: command not found
file: line 2134: },: command not found
file: line 2135: "node_modules/@hapi/hoek":: No such file or directory
file: line 2136: "version":: command not found
file: line 2137: "resolved":: command not found
file: line 2138: "integrity":: command not found
file: line 2139: "deprecated":: command not found
file: line 2140: },: command not found
file: line 2141: "node_modules/@hapi/joi":: No such file or directory
file: line 2142: "version":: command not found
file: line 2143: "resolved":: command not found
file: line 2144: "integrity":: command not found
file: line 2145: "deprecated":: command not found
file: line 2146: "dependencies":: command not found
file: line 2147: "@hapi/address":: No such file or directory
file: line 2148: "@hapi/bourne":: No such file or directory
file: line 2149: "@hapi/hoek":: No such file or directory
file: line 2150: "@hapi/topo":: No such file or directory
file: line 2151: syntax error near unexpected token `}'
file: line 2151: `      }'

看起来它忽略了反斜杠,或者它们与非转义引号交换,然后获取。 我的意思是,我希望echo "\"lorem\"\"ipsum\"" 会导致"lorem""ipsum",而不是 lorem command not found ipsum command not found

免责声明:我不是要求为我编写代码或调试我的代码(很遗憾,我真的必须指定这一点)

【问题讨论】:

  • 不要用 sed 解析 json。使用jq。整个你的帖子似乎对引用在 shell 中的工作方式有所误解 - 不,双重转义 value 不会改变任何东西。要转义稍后获取的值,请使用printf "%q" "$a"。但是我看不到目的-只是LOCK=$(cat file.json),将其写入文件有什么意义? I need to avoid command execution during the sourcing tho 为什么? create conf file干什么用的?
  • 这段代码反映的误解与BashFAQ #50背后的误解相同
  • @KamilCuk 我确实对报价的工作方式有误解:我认为" ' ' " 等于' " " '。但是我不需要解析任何东西,我需要按原样使用它,我并不关心里面是什么。
  • @CharlesDuffy 哇,这很有用,谢谢

标签: json string bash variables


【解决方案1】:

有没有更好的办法?

如果您想要将值输出到文件以供以后获取,无论变量值如何,我都喜欢使用declare -p

lock=$(cat myjson.json)
declare -p lock >> file_to_be_sourced.sh 

declare 特定于 Bash,它总是会输出正确引用的字符串,以便以后获取。另一种方法是使用printf "%q"

你知道发生了什么吗?

"s/'/\\\'/g" 部分错误。如果您以 ' 引号开头,则将 ' 替换为 '\''\' 引号内时是文字。并删除 s/"/\\"/g 部分 - 只需保留在 ' 引号内。使用来自 coreutils 与 printf 作为 bash 内置的 /bin/printf 时,观察到的差异很酷 - 它们使用不同的引用“样式”:

$ var="a\"b'c"
$ echo "$var"
a"b'c
$ printf "%q\n" "$var"
a\"b\'c
$ /bin/printf "%q\n" "$var"
'a"b'\''c'
$ declare -p var
declare -- var="a\"b'c"

如果我的要求是不可能的,有没有其他方法可以将文件用作字符串变量存储?

如果它是一个包含许多字符串的“存储”,您是否可以使用关联数组。

declare -A mapvar
mapvar["abc"]=$(< file1.json)
mapvar["def"]=$(< file2.json)
declare -p mapvar > file_to_be_sourced.sh

【讨论】:

    【解决方案2】:

    我终于找到了解决办法。 显然,如果您 echo "Something='$a'" 在该变量中是否有单引号、双引号、三重引号、转义引号或任何引号都没关系:他只是不在乎并使用双引号。 例如,在我的情况下,他只是忽略了最外面的',并将其他所有内容都视为命令

    我的解决方案很容易:

    echo "Something:\"$a\""
    

    现在它被视为一个实际的字符串。 我很困惑为什么,但这可能是由于 bash 如何使用引号引起的

    【讨论】:

    • @CharlesDuffy 如问题中所述,我只需将 json 文件的文本存储在变量中即可。不需要对对象进行操作,看这更像是一个json的备份,你不想知道foo.bar.foobar.barfoo[12312]的值,你只想复制json文件中的任何内容
    • 如果您有 bash 5.0 或更高版本,echo "variable=${variable@Q}" 将生成一个正确引用以供重复使用的分配。如果你不这样做,printf 'variable=%q\n' "$variable"。在任何情况下都不适合使用sed 更改引号。
    • @CharlesDuffy 谢谢你的建议
    • 另外,jq可以生成shell命令。例如jq -r '"var=\(. | tojson | @sh)"' &lt;in.json——如果它的输出是有源的,它将生成一个shell变量var,其内容为in.json
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-25
    • 1970-01-01
    • 2021-10-07
    • 1970-01-01
    • 2012-08-26
    • 2012-07-26
    • 2019-07-14
    相关资源
    最近更新 更多