【问题标题】:BASH shell expand arguments with spaces from variable [duplicate]BASH shell用变量中的空格扩展参数[重复]
【发布时间】:2017-01-27 21:08:04
【问题描述】:

假设我有一个变量$ARGS,其中包含以下内容:

file1.txt "second file.txt" file3.txt

如何将$ARGS 的内容作为参数传递给命令(例如cat $ARGS),将"second file.txt" 作为一个参数而不将其拆分为"secondfile.txt"

理想情况下,我希望能够将参数传递给任何命令,就像它们存储在变量中一样(从文本文件中读取,但我认为这不相关)。

谢谢!

【问题讨论】:

  • 不要使用变量;使用数组:ARGS=( "file1.txt" "second file.txt" "file3.txt" ),然后传递它:"${ARGS[@]}"
  • 如果设置在文本文件中并且参数之间的分隔符是空格,我不明白您为什么认为您看到的行为不正确。我的意思是我可以看到你想做什么,但是从文本文件中读取空格分隔的参数可能是错误的解决方案。如果您的需求很复杂,也许可以使用某种结构化文件格式。

标签: bash shell unix arguments spaces


【解决方案1】:

可以在没有 bash 数组或 eval 的情况下执行此操作:这是在没有 -0-d 扩展的情况下 xargs 的行为(主要会产生错误的行为)的少数地方之一确实有用。

# this will print each argument on a different line
# ...note that it breaks with arguments containing literal newlines!
xargs printf '%s\n' <<<"$ARGS"

...或...

# this will emit arguments in a NUL-delimited stream
xargs printf '%s\0' <<<"$ARGS"

# in bash 4.4, you can read this into an array like so:
readarray -t -d '' args < <(xargs printf '%s\0' <<<"$ARGS")
yourprog "${args[@]}" # actually run your programs

# in bash 3.x or newer, it's just a bit longer:
args=( );
while IFS= read -r -d '' arg; do
    args+=( "$arg" )
done < <(xargs printf '%s\0' <<<"$ARGS")
yourprog "${args[@]}" # actually run your program

# in POSIX sh, you can't safely handle arguments with literal newlines
# ...but, barring that, can do it like this:
set --
while IFS= read -r arg; do
    set -- "$@" "$arg"
done < <(printf '%s\n' "$ARGS" | xargs printf '%s\n')
yourprog "$@" # actually run your program

...或者,让xargs 自己执行调用:

# this will call yourprog with ARGS given
# ...but -- beware! -- will cause bugs if there are more arguments than will fit on one
# ...command line invocation.
printf '%s\n' "$ARGS" | xargs yourprog

【讨论】:

    【解决方案2】:

    正如 Jonathan Leffler 所提到的,您可以使用数组来做到这一点。

    my_array=( "file1.txt" "second file.txt" "file3.txt" )
    cat "${my_array[1]}"
    

    数组的索引从 0 开始。因此,如果您想 cat 数组中的第一个文件,您可以使用索引号 0。"${my_array[0]}"。如果要对所有元素运行命令,请将索引号替换为 @*。例如,您可以使用"${my_array[@]}"而不是"${my_arryay[0]}",确保引用数组,否则它将把任何带有空格的文件名视为单独的文件。

    或者,如果由于某种原因引用数组有问题,您可以将 IFS(代表内部字段分隔符)设置为等于换行符。如果您这样做,最好在更改默认 IFS 之前将其保存到变量中,这样您就可以将其设置回脚本完成后的状态。例如:

    # save IFS to a variable    
    old_IFS=${IFS-$' \t\n'}
    #set IFS to a newline
    IFS='$\n'
    
    # run your script
    my_array=( "file1.txt" "second file.txt" "file3.txt" )
    cat ${my_array[1]}
    
    # restore IFS to its default state
    IFS=$old_IFS
    

    除非万不得已,否则最好不要乱用 IFS。如果您可以引用数组以使您的脚本正常工作,那么您应该这样做。

    如需更深入地了解如何使用数组,请参阅:

    【讨论】:

    • 请考虑使用 ABS 以外的来源——rather notorious 用于在其示例中使用不良做法。有the bash-hackers page on arraysthe BashGuide on arraysBashFAQ #5
    • 另外,如果您的原始 IFS 值未设置,您在此处执行的 old_IFS shuffle 将不会按您的预期运行。考虑old_IFS=${IFS-$' \t\n'},如果原始值未设置(而不是设置为空字符串),则将默认值存储在old_IFS中。
    • 感谢您的信息。我已经更新了答案。
    • 顺便说一句,IFS='$\n'IFS=$'\n' 非常不同。在前一种情况下,您的IFS 是三个字符,$、反斜杠和n。在后者中,它是一个字符,一个文字换行符。
    • 我在这里偶然发现,找到了这个答案,并且能够解决一个长期存在的错误。为什么无数其他改变IFS的SO答案不小心恢复是令人费解的。谢谢!
    【解决方案3】:

    没有bashisms,纯shell 代码可能需要eval

    # make three temp files and list them.
    cd /tmp ;  echo ho > ho ; echo ho ho > "ho ho" ; echo ha > ha ; 
    A='ho "ho ho" ha' ; eval grep -n '.' $A
    

    输出:

    ho:1:ho
    ho ho:1:ho ho
    ha:1:ha
    

    注意eval 功能强大,而if not used responsibly can lead to mischief...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-29
      • 1970-01-01
      • 2018-05-29
      • 2015-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多