【问题标题】:Assumption with double quotes, single quotes & no quotes带双引号、单引号和不带引号的假设
【发布时间】:2016-04-27 09:18:31
【问题描述】:

在 bash 中:

使用双引号

echo "*" #Globbing is never done
echo "$variable" # Variable expansion is always done
echo "$(command)" # command expansion is always done

使用单引号

echo '*' #Globbing is never done 
echo '$variable' # Variable expansion is never done
echo '$(command)' # command expansion is never done

不使用引号

echo * #Globbing always done.
echo $variable; # Variable expansion is always done
echo $(command) # command expansion is always done

这适用于所有命令吗?

【问题讨论】:

  • '!$"*<> 通常是这些符号的优先顺序。
  • 这些假设总是正确的——更有趣的地方是关于是否/如何对 results 执行其他处理步骤,例如字符串拆分或通配符这些扩展。毕竟,"$foo"$foo 根本不是一回事,尽管两者总是发生扩展。
  • 另外,重要的是要注意引号是每个字符的属性,因此可以执行 "$foo"/* 之类的操作
  • 太有趣了,我从其他人的答案中学到了一些东西:-)

标签: bash


【解决方案1】:

您似乎正在寻找例外情况,我猜您已经想到了一些例外情况。我将假设set -f/set -o noglob 被排除在此案例之外?

当您使用dd 命令时,即使不加引号,也不会发生通配。

$ ls *.txt
blah.txt  file1.txt  file2.txt  file.txt  logfile.txt
$ dd if=*.txt of=glob.txt
dd: failed to open ‘*.txt’: No such file or directory

反驳和误报

这里有一些奇数的例子,但遵循扩展

variable='2010-09-08 12:34:56' echo "$variable" | xargs date +%s -d date: extra operand '12:34:56'

额外的操作数表明正在发生变量扩展,您正在丢失管道中的引号。 $ date +%s -d 2010-09-08 12:34:56 date: extra operand ‘12:34:56’

如果您为echo $1 创建一个脚本,然后在传递时扩展您引用的变量,也会发生这种情况。它会扩展,并按预期工作。因此,问题不在于 xargs,而在于您在管道之前的扩展是正常的。

  1. Eval... evals 的全部目的是在运行命令之前对其参数进行扩展。 bash -c 也会发生扩展,但它需要一个参数。所以,再次强调,这不是扩展问题,而是命令使用问题。

cmd='printf "%s\n" "$(date -d "$variable" +%c)"' bash -c $cmd

与扩展版相同

$ bash -c printf "%s\n" "$(date -d "$variable" +%c)" printf: usage: printf [-v var] format [arguments]

  1. 我真的很喜欢 Hauri 的 $'...' 和 $"..." 信息——但是,我们正在谈论的不是这些。事实上,它们的行为正如 bash 手册页所说的那样。 $'' 与 '' 的不同就像 (()) 与 $(()) 的不同

  2. 我对这个很兴奋,所以... $ ls mclark.txt qt sign_in.txt skel.bash $ zip m*t.zip *t $ ls *.zip m*t.zip

但是,这也不对 - splat 会扩展,但在不匹配时 zip 将其用作文字。我找到了一些执行此操作的命令,但如果有匹配项(我稍后添加了 my.zip),它会使用匹配的扩展名(引发错误,b/c my.zip 是用于测试目的的文本文件)。

【讨论】:

  • 事实上我是!如果时间允许,请考虑添加更多。 :D
  • 我找到了一些那个 glob,但是如果 glob 不匹配,那么它将它用作字符串文字。我认为这不符合要求,因为它首先是 globbing,但有偶然性。非常难的问题。
  • 创建名为 if=foo.txtif=bar.txt 的文件并再次尝试您的 dd 示例。
  • 这个答案很长,但只提供虚假信息。否决。 (向我展示该答案中正确解释的一段代码,我会考虑删除反对票)
【解决方案2】:

存在多种力量。一般来说,您可以假设单引号是为了隐藏 bash 扩展的内容。双引号是对可能有空格的值进行分组,以便 bash 将它们视为一个逻辑单元,但也会禁用通配符。不过有很多注意事项...

用双引号括起来的字符保留字面值 引号内的所有字符,除了 $、'、\ 和, 当启用历史扩展时,!。字符 $ 和 ' 保留 它们在双引号中的特殊含义。反斜杠保留 仅当后跟以下之一时,其特殊含义 字符:$、'、"、\ 或 . 双引号可以被引用 在双引号内加上反斜杠。如果启用, 除非 !出现在双 引号使用反斜杠进行转义。 ! 前面的反斜杠 没有被删除。

参见man bash的引用部分

下面这个例子,会让你更加困惑或者更清楚。

$ echo "*test*"
*test*

$ echo '*test*'
*test*

$ msg=$(echo '*test*')
$ echo '$msg'
$msg

$ echo "$msg"
*test*

$ echo $msg
speedtest test1 test2 test3 test4 test5 testdir

请注意,如果没有匹配项,它将打印 *test* 而不是 Hastur 评论的空行。

其他一些有趣的花絮

请注意,这不起作用

$ echo 'single quotes don\'t escape'

但是这行得通

$ echo "\"double quotes\" escape"

但你可以在不转义的情况下使用其中一个

$ echo '"' "'"

【讨论】:

  • 您还应该提到双引号可以防止通配符。它隐含在您引用的段落中,但您的摘要错过了使用引号的这一半常见原因。
  • 注意: echo $msg 将回答 *test* 如果没有匹配的扩展(很简单,如果没有名称兼容的文件,它将回答*test* 否则 test01 test02 ...
  • @Hastur 这取决于nullglob shell 选项的设置:打开时,不成功的全局匹配扩展为空字符串,关闭时,它扩展为自身。
  • 您还可以结束并重新启动字符串以添加单个(转义)引号:echo 'You can hack single quotes that don'\''t escape in'
  • @BenjaminW 对:如果nullglob 开启且没有匹配项,该命令将给出一个空字符串,如果nullglob 关闭,它将返回字符串*test*默认关闭。 此外,如果failglob 开启(默认关闭),则会生成错误并打印消息。从 man bash:`如果没有找到匹配的文件名,并且 shell 选项 nullglob 未启用,则单词保持不变。如果设置了 nullglob 选项,但没有找到匹配项,则删除该单词。如果设置了 failglob shell 选项,但没有找到匹配项,则会打印一条错误消息并且不执行该命令。
【解决方案3】:

简短回答:是的

这个假设是基本上正确的,永远!

variable='2010-09-08 12:34:56' 
vname=variable
date -d "$variable" +%s
1283942096

date -d "${!vname}" +%s
1283942096

date -d $variable +%s
date: extra operand '+%s'
Try 'date --help' for more information.

date -d '$variable' +%s
date: invalid date '$variable'

date -d ${!vname} +%s
date: extra operand '+%s'
Try 'date --help' for more information.

但是

  1. xargs 这样的一些命令在扩展 和参数分布方面发挥了作用。

    echo "$variable" | xargs date +%s -d
    date: extra operand '12:34:56'
    Try 'date --help' for more information.
    

    你必须使用-0 arg 到xargs

    echo "$variable" | xargs -0 date +%s -d
    1283942096
    
  2. 内置命令可以不同地使用 args,尤其是 eval:

    cmd='printf "%s\n" $(date -d "$variable" +%c)'
    eval $cmd
    Wed
    Sep
    8
    12:34:56
    2010
    
    cmd='printf "%s\n" "$(date -d "$variable" +%c)"'
    eval "$cmd"
    Wed Sep  8 12:34:56 2010
    
    eval $cmd
    Wed Sep  8 12:34:56 2010
    
    bash -c "$cmd"
    Mon May 16 00:00:00 2016
    
    bash -c $cmd
    printf: usage: printf [-v var] format [arguments]
    
  3. bash下有趣的东西的语法不限于".."'..'${}

    • $'...' 让你打印特殊字符,但不要扩展变量:

      echo $'This\tis\ta string containing ${variable}'
      This    is  a string containing ${variable}
      
    • 反引号:为了兼容性,始终支持反引号。如果不是很可读,您可能会在某些脚本中看到:

      echo `date +%c -d "${!vname}"`
      Wed Sep 8 12:34:56 2010
      
    • 语法$"..." 可用于本地化:

      export TEXTDOMAIN=bash
      export LANG=fr_CH.utf-8
      echo $"Running"
      En cours d'exécution
      

【讨论】:

  • eval 遵循规则——它的全部功能是在正常执行之前进行一层扩展。就像手册页中描述的那样。
【解决方案4】:

如果没有匹配 *.xtx 而 a.txt 是一个文件 mv a.txt *.xtxt 也会给你带来意想不到的结果。 这同样适用于 cp 之类的其他内容,甚至将其视为引用:

$ ls *.xtx
/bin/ls: cannot access *.xtx: No such file or directory
$ echo "A" > *.xtx
$ ls *.xtx
*.xtx
$

虽然它们都应该返回错误,就像如果有多个文件一样,您会得到“不明确的重定向”。

【讨论】:

  • If nothing matches 表示首先完成通配。有趣的案例。
  • 没有移动的目的地应该只给出错误而不做任何其他事情。现在不带引号的 * 应该总是做 globbing,所以移动应该缺少目的地并返回错误......相反,它将通配符视为被引用......所以这完全适合问题不适用的命令。跨度>
【解决方案5】:

这适用于所有命令吗?

是的。

来自 Bash 参考手册:

3.1.2.2 Single Quotes

将字符括在单引号 (') 中会保留文字 引号内每个字符的值。单引号不能 出现在单引号之间,即使前面有反斜杠。

3.1.2.3 Double Quotes

用双引号括起来的字符 (") 保留文字 引号内所有字符的值,$ 除外, `、\ 和,当启用历史扩展时,!。那些角色 $ 和 ` 在双引号中保留其特殊含义(请参阅 Shell Expansions)。反斜杠仅保留其特殊含义 后跟以下字符之一时:$、`、"、\、 或换行符。在双引号内,后跟一个反斜杠 这些字符被删除。反斜杠前面的字符 没有特殊含义的不加修饰。双引号可能是 用反斜杠在双引号中引用。如果 启用,将执行历史扩展,除非!出现 在双引号中使用反斜杠进行转义。前面的反斜杠 这 !没有被删除。

特殊参数 * 和 @ 在 double 中具有特殊含义 引号(见Shell Parameter Expansion)。

【讨论】:

  • @louigi600 $ set -- 1 2 3 4 5 6 7 8 9 0 a b c d e f g h, $ echo '${@:7}' "${@:7}"
  • ~$ echo "*" ; * ; ~$ echo "@" ; @; ~$ 回声 $SHELL ; /bin/bash ; 〜$ ;也许并不总是有特殊意义
  • @louigi600 是的,这取决于上下文。
【解决方案6】:

可能 shell 参考手册和 shell 手册页包含预期的行为......但结果可能并不总是最初的预期。

阅读手册页的“QUOTING”部分也很有趣。这是 bash 手册页中有关单引号和双引号的部分:(与参考手册的内容几乎相同)

用单引号括起来的字符保留字面值 引号内的每个字符。可能不会出现单引号 在单引号之间,即使前面有反斜杠。

用双引号括起来的字符保留字面值 引号内的所有字符,除了 $、`、\ 和, 当启用历史扩展时,!。字符 $ 和 ` 在双引号内保留它们的特殊含义。反斜杠 仅当后跟以下之一时才保留其特殊含义 字符:$、`、"、\ 或 . 双引号可能是 用反斜杠在双引号中引用。如果 启用时,将执行历史扩展,除非! 出现在双引号中的使用反斜杠进行转义。这 ! 前面的反斜杠没有被删除。

【讨论】:

  • 我想看看它不遵守这些规则的一些例子。
  • 没有人说我知道任何这样的例子......但是随着时间的推移可能会发生这样的事情。只是引用我的一个想法:我不认为打击 bash 的 LOCALE 问题是有意的,但最近需要大量关注才能修复它。手册页中还有这句话:“特殊参数 * 和 @ 在双引号中具有特殊含义(请参阅下面的参数)。”这为解释留下了余地......它仅指位置参数,但如果应用于一般回声“*”没有特殊含义,并且与回声“@”相同。
  • 对于数组 * 和 @ 从 0 开始而不是从 1 开始。
  • bash 的手册页告诉你数组是从零开始的,就像我所知道的所有编程语言一样。
  • 也许你从来没有用过帕斯卡。
猜你喜欢
  • 2017-05-03
  • 2017-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多