Shell 编程中,何时需要双引号?

2017-03-12|Categories: Linux|

示例shell脚本

#!/bin/bash

# read-ifs: change ifs (internal fields separator) then read input from a file

FILE=/etc/passwd
read -p "Please enter a user name > " user_name
user_record=$(grep "^$user_name:" $FILE)

if [[ -n "$user_record" ]]; then
    OLD_IFS=$IFS
    IFS=":"
    # 如果 "$user_record" 没有双引号,read 读取的值会全部赋值给第一个变量 user
    read user pw uid gid fname home shell <<< "$user_record"  
    cat <<- _EOF_
            User = $user
            UID = $uid
            GID = $gid
            Full name = $fname
            Home dir. = $home
            Shell = $shell
            _EOF_
    IFS=$OLD_IFS
else
    echo "No such user: $user_name" >&2
    exit 1
fi
exit

总是使用双引号包围「变量替换」和「命令替换」

以下内容从 http://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-characters 节选并翻译

如果不使用双引号,当输入或参数包含空白或特殊字符\[*?时,脚本执行会失败。

这个答案适用于Bourne/POSIX风格的shell(shashdashbashkshmkshyash……)。Zsh用户应该跳过它并阅读何时必须使用双引号?代替。如果你想了解所有情况,请阅读标准或您的shell的手册。

为什么我需要写"$foo"?没有引号会怎么样?

$foo并不仅仅表示「取变量foo的值」。它的意思要复杂得多:

  • 首先,取出变量的值。
  • 字段拆分:将该值视为空格分隔的字段列表,并生成结果列表。例如,如果变量包含foo*bar,则此步骤的结果是3元素列表foo*bar
  • 文件名生成:将每个字段视为glob,即通配符模式,并将其替换为与该模式匹配的文件名称列表。如果模式与任何文件不匹配,则不会被修改。在我们的例子里,列表包含foo,接下来是当前目录的所有文件的列表,最后是bar。如果当前目录没有文件,列表就是foo*bar

请注意,结果是字符串列表。shell语法有两个上下文:列表上下文和字符串上下文。字段拆分和文件名生成仅在列表上下文中发生,但这是大部分时间。双引号分隔字符串上下文:整个双引号字符串是单个字符串,不分割。(例外:"$@"扩展到位置参数列表,例如"$@"相当于"$1" "$2" "$3",如果有三个位置参数的话。请参阅$*$@的区别。)

$(foo)`foo`命令替换也是一样。注意,不要使用`foo`:它的引用规则是奇怪的,不可移植的,所有现代shell都支持$(foo),除了具有直观的引用规则之外,(在各种现代shell)绝对是等价的。

算术替换的输出也经历相同的扩展,但这通常不是一个问题,因为它只包含不可扩展的字符(假设IFS不包含数字或-)。

你只要记住 总是 使用双引号围绕变量和命令替换,除非你了解所有这些将要发生的繁琐情况。千万要小心:不使用引号不仅会导致错误,还会导致安全漏洞

其他与shell引号相关的问题

  • 浏览StackExchange网站上的/quoting标签,或者是/shell/shell-script标签。(点击「learn more…」,查看一些常见提示和手工选择的常见问题列表。)
  • 《Unix & Linux大学教程》第13章13.3节:强引用和弱引用

备注

  • 这里的「substitutions」也被翻译为「展开」,个人更喜欢「替换」,更加直观。
  • 在计算机编程中,特别是在类Unix环境中,glob模式指的是「具有通配符的文件名集合」。例如,Unix命令mv *.txt textfiles/移动(mv)当前目录所有以.txt结尾的文件到textfiles目录。这里,*是一个通配符,代表「任何字符串」,*.txt是一个glob模式。详见:https://en.wikipedia.org/wiki/Glob_(programming)

Leave A Comment