【问题标题】:Tokenizing a string in bash without using redirect在不使用重定向的情况下在 bash 中标记字符串
【发布时间】:2020-04-26 02:01:09
【问题描述】:

在我的脚本中,我使用简单的一行来标记:

IFS=';' read -ra ALL_ADDR <<< "${SERVER}"

但是,当我在busybox(alpine)linux docker容器中运行此脚本时,它会在上面的行中跌跌撞撞。 line 43: syntax error: unexpected redirection

我理解这是因为脚本不是由Bash 运行的,而是由Ash 运行的,它没有&lt;&lt;&lt; 重定向参数。如何将上面的行转换为在所有流行的 shell 脚本中都可以使用的代码。 (主要是 Bash、Ash 和 Dash)?

为了完整起见,变量如下:

SERVER="/api/v1:127.0.0.1:1337;/api/v2:127.0.0.1:1338"
IFS=';' read -ra ALL_ADDR <<< "${SERVER}"
for i in "${ALL_ADDR[@]}"; do
    IFS=':' read -ra ADDR <<< "${i}"
    PROXY=${ADDR[0]}
    IPADDR=${ADDR[1]}
    PORT=${ADDR[2]}
    LOC_STRING="${LOC_STRING}\tlocation ${PROXY} {\n\t\tproxy_pass http://${IPADDR}:${PORT}\n\t}\n"
done
LOC_STRING=${LOC_STRING//\//\\\/}

#"insert" above generated lines into a site config for nginx
sed -e "0,/^\s*location/{s/^\s*location/${LOC_STRING}\n&/}" 'portal' > "../sites-enabled/portal"

【问题讨论】:

  • 这能回答你的问题吗? Bash: How to tokenize a string variable?
  • 该问题中的答案都不是特别安全的,其中一些使用了ash 不支持的数组。
  • 您可以使用 here-doc 重定向来获得相同的效果,但重定向问题并不是唯一的:ashread 不支持 -a 标志。您可能必须使用while read -r ADDR; do [...]; done &lt;&lt;DELIM; $SERVER; DELIM 循环才能达到相同的效果。
  • var 的内容是什么样的?
  • @PaulHodges 我用“用例”更新了这个问题。

标签: bash shell ash


【解决方案1】:

您似乎并不真的需要对其进行标记。只需生成结果字符串。

下面我首先将每个:; 替换为换行符。然后使用 xargs 我一次读取 3 个变量并传递给 printf 格式。 sed 's@/@\\/@g' 的作用与 LOC_STRING=${LOC_STRING//\//\\\/} 相同。

SERVER="/api/v1:127.0.0.1:1337;/api/v2:127.0.0.1:1338"
LOC_STRING=$(
    printf "%s\n" "$SERVER"  |
    tr ':;' '\n\n' |
    xargs -d$'\n' -n3 printf '\tlocation %s {\\n\t\tproxy_pass http://%s:%s\\n\t}\\n' |
    sed 's@/@\\/@g'
)

echo "$LOC_STRING"

输出:

    location \/api\/v1 {\n      proxy_pass http:\/\/127.0.0.1:1337\n  }\n location \/api\/v2 {\n      proxy_pass http:\/\/127.0.0.1:1338\n  }\n

要“标记化”,最简单的方法是插入换行符代替分隔符,并将其作为换行符分隔的流读取。如果字符串本身有换行符,则会出现问题,但从IFS=';' read -ra ALL_ADDR &lt;&lt;&lt; "${SERVER}" 来看,SERVER 中没有换行符,或者您只对第一行感兴趣。如此相似 - 将 ;: 替换为换行符。然后一次只阅读3个项目。使用 &lt;&lt; 重定向 posix shell 中的某些内容。

tmp=$(printf "%s\n" "$SERVER" | tr ';:' '\n\n')
while IFS= read -r PROXY &&
        IFS= read -r IPADDR &&
        IFS= read -r POST; do
    LOC_STRING="${LOC_STRING}\tlocation ${PROXY} {\n\t\tproxy_pass http://${IPADDR}:${PORT}\n\t}\n"
done <<EOF
$tmp
EOF
LOC_STRING=${LOC_STRING//\//\\\/}

注意:按照惯例,大写变量用于exported 的变量。对于脚本中的局部变量,请使用小写变量。

【讨论】:

    【解决方案2】:

    由于这只是询问这里的字符串,我只是顺便提一下,您确实不应该在旨在可移植的脚本中使用数组。暂时忽略这个问题,你总是可以用 heredoc 替换 herestring:

    IFS=';' read -ra ALL_ADDR << EOF
    ${SERVER}
    EOF
    

    请注意,您真的不应该大喊大叫,这样写会更好:

    IFS=';' read -ra all_addr << EOF
    ${server}
    EOF
    

    【讨论】:

    • ash 也不支持数组。
    • @chepner 是的,因此是第一句话!也许我应该更明确一点。
    【解决方案3】:

    我可能不会直接回答你的问题,但是sed 解决方案怎么样:

    SERVER="/api/v1:127.0.0.1:1337;/api/v2:127.0.0.1:1338"
    LOC_STRING=$(echo "$SERVER" | tr ';' '\n' | sed -e 's#\([^:]*\):\([^:]*\):\([^:]*\)#\tlocation \1 {\n\t\tproxy_pass http://\2:\3\n\t}#' -e 's#/#\\/#g')
    

    echo "$LOC_STRING" 的输出:

        location \/api\/v1 {
            proxy_pass http:\/\/127.0.0.1:1337
        }
        location \/api\/v2 {
            proxy_pass http:\/\/127.0.0.1:1338
        }
    

    请注意,双引号中的单词\nash 中被解码为换行符,而在bash 中则保持原样。可能需要稍作调整 根据您的要求。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-07
      • 1970-01-01
      • 1970-01-01
      • 2011-10-20
      • 1970-01-01
      • 1970-01-01
      • 2013-11-04
      • 2023-03-20
      相关资源
      最近更新 更多