【问题标题】:How do I learn how to get quoting right in bash?我如何学习如何在 bash 中正确引用?
【发布时间】:2012-04-03 13:32:51
【问题描述】:

在编写 bash 脚本时,我经常对引用和评估的规则感到困惑。我知道一些基础知识,例如 '' 和 "" 和 `` 之间的区别,但我似乎仍然经常弄错,并且只能尝试各种不同的方式来表达同一件事。

我通常可以通过蛮力解决任何个别问题,但我认为我的概念模型必须以某种未知的方式被彻底打破。

我对 lisp 的引用、评估、读取、打印、语法引用系统没有任何问题。事实上,我写了一些 kata 来帮助人们理解发生了什么:http://www.learningclojure.com/2010/11/syntax-quote-kata-for-confused.html

我想我正在为 bash 寻找类似的东西(这看起来要复杂得多)。一个好的模型或一组练习将帮助我形成这样一个模型,这将使​​我能够查看一个复杂的 shell 脚本,其中变量正在被转换、评估、打印和读取,并计算出会发生什么无需尝试。

如果做不到这一点,调试过程的好方法,以及观察评估的每个阶段发生的情况,将非常有帮助。

【问题讨论】:

  • 阅读manual
  • 避免反引号并改用$() ...这已经消除了很多引用注意事项...

标签: bash eval quote


【解决方案1】:

Bruce Barnett's UNIX Shell Quote Tutorial 很棒,Bash FAQ/pitfalls/word splitting 文章有大量有用的提示。一个简短的总结:

不带引号的 字符串可以包含大多数字符,但不是所有字符(如换行符),其中许多字符(包括空格)必须被转义。只是不要使用它们 - 如果您中了这个诱惑,您可能会发现修改脚本的人在必要时忘记包含引号。

单引号字符串可以包含大多数字符,包括 NUL 和换行符,但不能包含单引号,因此它们也仅对简单值有用。

反引号用于命令。仅当您的 shell 不支持 $() 时才应使用它们。示例:

current_dir=`pwd` # BAD! Don't do this!

那个命令不好,因为当赋值的右边没有被引用时,shell 会在它上面执行word splitting。它通常会导致难以重现的错误,因为空格很难目视检查。要引用命令,您必须使用双引号

current_dir="$(pwd)" # OK, but loses newlines at EOF

EOF 的换行符特别棘手。您可以添加单个字符并使用例如

将其剥离
# Works for some commands, but not pwd
current_dirx="$(pwd; echo x)"
current_dir="${current_dirx%x}"
printf %s "$current_dir"

,但还有一个额外的困难,因为某些命令(例如 pwd)会在其输出的末尾添加一个换行符无论如何,因此您可能还必须删除它:

# Works for some commands, including pwd
current_dirx="$(pwd; echo x)"
current_dir="${current_dirx%$'\nx'}"
printf %s "$current_dir"

双引号可以包含任何字符(试试echo -ne "\0" | wc -c),但请注意变量不能包含NUL字符。

ANSI-C quotes 可以包含任何字符NUL 除外(试试echo -ne $'\0' | wc -c),并提供方便的转义码以更轻松地处理特殊字符:

printf %s $'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n'
printf %q $'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n'
touch -- $'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n'
rm -- $'--$`!*@\a\b\E\f\r\t\v\\\'"\360\240\202\211 \n'

【讨论】:

    【解决方案2】:

    使用单引号 '' 引用原始文本(即使反斜杠也不会转义单引号中的任何内容):

    > echo '\'
    \
    > echo '$PATH'
    $PATH
    

    使用双引号 "" 引用包含 shell 应评估的内容的文本,例如变量 ($bla)、子 shell 调用 ($(ls)) 和评估 ($((5 + 3)))。

    > echo "$PATH"
    /home/alfe/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
    > echo "$(ls | tail -1)"
    bla
    > echo "$((5 + 3))"
    8
    

    如果由于某种原因您不能使用$(),则使用反引号``(例如,在极少数情况下,您必须使用sh 而不是bash。通常使用$() 来收集子shell 的输出进入当前命令。

    > echo "$(ls | tail -1) is the last file in the current dir."
    bla is the last file in the current dir.
    

    我在使用其他人的 bash 代码时遇到的主要问题之一是缺少双引号,这通常只是一个单词,但在极少数情况下可能不止一个单词,或者可能包含特殊字符。所以尽可能使用双引号。

    > a="four    spaces"
    > echo $a
    four spaces
    > echo "$a"
    four    spaces
    

    【讨论】:

    • 请注意“旧格式 $[expression] 已弃用,将在即将发布的 bash 版本中删除。” (来自man bash) - 改用$((expression))
    【解决方案3】:

    在 shell 提示符下,

    set -x
    set -v
    

    并创建以下 python 程序 'args.py'

    #!/usr/bin/env python
    
    import sys
    print sys.argv
    for arg in sys.argv[1:]:
        for c in arg:
            print c,"|",
        print
    

    然后尝试以下命令调用:

    U=hello\ world ; V="-->$U<--"; W="1 $U 2 $V 3"; args.py $W
    

    直到你意识到没有逻辑的方式来思考正在发生的事情。这真的都是由反复无常的魔法贝壳小精灵完成的。

    【讨论】:

    • 发生了什么是word splitting - 您只需使用引号和/或数组将一组变量作为参数传递给shell脚本:U=hello\ world; V="--&gt;$U&lt;--"; W=("1" "$U" "2" "$V" "3"); args.py "${W[@]}"
    猜你喜欢
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    • 2010-10-04
    • 2022-12-11
    • 1970-01-01
    • 2020-05-03
    • 2021-04-15
    • 1970-01-01
    相关资源
    最近更新 更多