3

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

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

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

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

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

4

3 回答 3

9

Bruce Barnett 的 UNIX Shell 引用教程很棒,B​​ash常见问题解答/陷阱/分词文章有大量有用的提示。一个简短的总结:

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

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

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

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

该命令很糟糕,因为当未引用赋值的右侧时,shell 会对其进行分词。它通常会导致难以重现的错误,因为空格很难目视检查。要引用命令,您必须使用双引号

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"

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

ANSI-C 引号可以包含除 NUL (Tryecho -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'
于 2012-04-03T13:50:38.277 回答
1

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

> 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
于 2012-04-03T14:07:22.950 回答
0

在 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

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

于 2012-04-12T10:48:38.907 回答