【问题标题】:Splitting strings on " creates an empty string [duplicate]在 " 上拆分字符串会创建一个空字符串 [重复]
【发布时间】:2018-06-01 07:39:05
【问题描述】:

我在我的 shell 脚本中使用了一个返回多个字符串的命令,每个字符串都包含在 "" 中。由于我需要将这些字符串中的每一个作为数组的单独元素,因此我使用" 作为分隔符来拆分这个字符串集合,如下所示:

IFS='"'
arr=($(command that returns multiple strings enclosed in ""))

现在,由于每个字符串的开头都有一个" 字符,我的脚本将每个字符串拆分为一个空白字符串和字符串本身。例如,字符串"foo" "bar" 将被拆分为(空字符串)、foo(又是空字符串)和bar。所以我的数组最终有 4 个元素,而不是 2 个。

有两种方法可以克服这个问题,任何实现的帮助都会有所帮助:

  1. 在拆分时以某种方式摆脱了空白。
  2. 使用空格创建数组,然后创建另一个数组,仅将第一个数组中的非空格元素插入到第二个数组中。

我将答案标记为 bash 和 ksh,因为 bash 也可以接受。谢谢!

【问题讨论】:

  • arr=( $(...) ) 从一开始就是不好的做法。实际上,从来没有一个地方应该做某事。
  • 如果你可以控制输出,你可以使用eval arr=($(echo '"foo" "bar"'))。但是,这可能具有潜在危险,例如eval arr=($(echo '"foo" "$(echo dangerous)"'))。 `
  • (如果您想在分隔符上拆分单词,请考虑使用 IFS='"' read -r -a arr <<<"$string" 代替——这样可以避免全局扩展。但是,它不是解析 shell 引用内容的正确工具)。

标签: string bash shell ksh


【解决方案1】:

除非引用的字符串包含换行符,否则您可以使用 xargs 将引用的字符串处理为 NUL 分隔的单词列表:

array=( )
while IFS= read -r -d '' piece; do
  array+=( "$piece" )
done < <(command-that-returns-multiple-quoted-strings | xargs printf '%s\0')

如果您要拆分的引用字符串确实包含换行符,xargs 将无法正常工作;请考虑使用 Python 标准库 shlex module

shell_quotes_to_NULs() {
  python -c '
import sys, shlex
for piece in shlex.split(sys.stdin.read()):
    sys.stdout.write(piece)
    sys.stdout.write("\0")
'
}

array=( )
while IFS= read -r -d '' piece; do
  array+=( "$piece" )
done < <(command-that-returns-multiple-quoted-strings | shell_quotes_to_NULs)

【讨论】:

    【解决方案2】:

    如果您想将字符串存储在双引号中而忽略其余部分,这里有一个awk 解决方案,可以正确处理&lt;newline&gt;

    arr=(); 
    while IFS= read -r -d '' item; do 
       arr+=("$item"); 
    done < <(cmd | gawk -v RS='"[^"]*"' 'RT { gsub("\"", "", RT); printf RT"\0"}');
    

    bash 4.4 或更高版本:

    readarray -d '' arr < <(cmd | gawk -v RS='"[^"]*"' 'RT { gsub("\"", "", RT); printf RT"\0"}')
    

    【讨论】:

    • 这会将"*" 替换为当前目录中的文件名列表,除非您在展开之前关闭通配。
    • @CharlesDuffy 是的。我之前的“解决方案”总体上只是一个仓促的尝试,它不能正常工作......我试图提出一个也接受&lt;newline&gt; 的解决方案,我已经更新了我的答案。
    • 如果放到通用用例中,我仍然认为这里的 awk 方法相对脆弱。如果我用"foo bar" "baz qux" one\ two three" "four 测试它,onetwothreefour 都不会出现在输出中,而xargs 一切正常。
    • @CharlesDuffy 嗯,它适用于 awk:declare -a arr='([0]="foo bar" [1]="baz qux" [2]=" ")'。这正是我想象的OP想要的。它会忽略不在引号中的字符串。
    • 啊——这不是我期望 OP 在这些情况下想要的(被标记为重复的问题是明确要求解析等同于 shell 的行为方式,即一个相当普遍的要求),但我想这是他们要回答的问题。 (当我读到这个问题时,他们预计引号之外除了空格之外没有任何其他内容,但 确实 希望保留字符串拆分后出现的除空格以外的所有内容。
    猜你喜欢
    • 2015-05-12
    • 2015-04-14
    • 2020-04-17
    • 2011-05-31
    • 2011-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    相关资源
    最近更新 更多