得之我幸 失之我命

when someone abandons you,it is him that gets loss because he lost someone who truly loves him but you just lost one who doesn’t love you.

bash 中单引号和双引号

踩坑,bash 的单引号和双引号的坑,首先给出结论,两者差距还是蛮大的

'" 的不同,主要是对于某些 meta 的关闭与否,以 $ 來作说明:

1
2
3
4
5
$ A=B\ C
$ echo "$A"
B C
$ echo '$A'
$A

在第一个 echo 命令行中,$ 被置于 soft quote 中,將不被关闭,继续处理变量替换,因此 echo 將 A 的变量值输出到屏幕,也就得到 “B C” 的结果

在第二个 echo 命令行中,$ 被置于 hard quote 中,则被关闭,因此 $ 只是一个 $ 符号,并不会用來作变量替换处理,因此结果是 $ 符号后面接一个 A 字母:$A

从上面的结论中不难产生一个疑惑:嵌套的情况下,会怎么解析呢

  1. 单引号中嵌套双引号

    1
    2
    3
    $ test_env=2
    $ echo '"$test_env"'
    $ "$test_env"
  2. 双引号中嵌套单引号

    1
    2
    3
    $ test_env=2
    $ echo "'$test_env'"
    $ '2'

背景

你在 shell prompt 键入内容,按下 enter 的时候,它们就是 command line 了,然后 shell 才会以进程方式执行你所提交的命令。但是,你知道你按下的键,对 shell 来说,有什么区别吗?

简单而言,command line 的每一个 charactor 分为如下两种:

  1. literal:也就是普通纯文字,对shell来说没有特殊功能
  2. meta:对shell来说,具有特定功能的保留字

literal 没有什么好说的,凡是 abcd、123456 等这些 “文字” 都是 literal

但是 meta 就不是那么清晰易懂了,在 command line 中已碰到两个几乎每次都会碰到的 meta:

  1. IFS(Internal Field Seperator):由 space tab enter 三者之一组成(我们常用space)
  2. CR(Carriage Return):由 enter 产生

IFS 是用来拆分 command line 的每一个词(word)用的,因为 shell command line 是按词来处理的

CR 则是用来结束 command line 用的,这也是为何我们敲 enter 命令就会执行的原因

除了IFS和CR外,常用的meta还有:

  1. = : 设定变量
  2. $ : 做变量或运算替换(请不要与 shell prompt 搞混了)
  3. > : 重定向 stdout
  4. < : 重定向 stdin
  5. |: 管道命令
  6. & : 重定向 file descriptor ,或将命令置于后台执行
  7. ( ): 將其內的命令置于 nested subshell 执行,或用于运算或命令替换
  8. { }: 將其內的命令置于 non-named function 中执行,或用在变量替换的界定范围
  9. ; : 在前一个命令结束时,而忽略其返回值,继续执行下一個命令
  10. && : 在前一個命令结束时,若返回值为 true,继续执行下一個命令
  11. || : 在前一個命令结束时,若返回值为 false,继续执行下一個命令
  12. !: 执行 history 列表中的命令

等等

假如我们要在 command line 中将这些保留元字符的功能关闭的话,就要用到 quoting 处理了

在 bash 中,我们常用的 quoting 有如下三种方法:

  1. *hard quote:‘’(单引号),凡在 hard quote 中的所有 meta 均被关闭
  2. *soft quote:“”(双引号),在 soft quote 中的大部分 meta 都会被关闭,但某些保留(如 $)
  3. *escape:\ (反斜线),只有紧接在 escape(跳脱字符)之后的单一 meta 才被关闭

下面的例子將有助于我们对 quoting 的了解:

1
2
3
4
5
6
7
$ A=B C    # 空白键未被关闭,作为 IFS 处理
$ C: command not found.
# 输出为空,A 赋值失败
$ echo $A
$ A="B C" # 空白键已被关闭,仅作空白符处理
$ echo $A
B C

在第一次设定 A 变量时,由于空白键没有被关闭,command line 将被解读为:A=B 然后碰到 IFS,再执行 C 命令

在第二次设定 A 变量时,由于空白键置于 soft quote 中,因此被关闭,不再作为 IFS :A=B space C

事实上,空白键无论在 soft quote 还是在 hard quote 中,均会被关闭。Enter 鍵亦然:

1
2
3
4
5
6
$ A='B
> C
> '
$ echo "$A"
B
C

在上例中,由于 enter 被置于 hard quote 当中,因此不再作为 CR 字符來处理。

这里的 enter 单纯只是一个断行符号(new-line)而已,由于 command line 并沒得到 CR 字符,因此进入第二個 shell prompt ,command line 并不会结束,直到第三行,输入的 enter 并不在 hard quote 里面,因此并沒被关闭,此时,command line 碰到 CR 字符,于是结束、交给 shell 來处理

上例的 enter 要是被置于 soft quote 中的话,CR 也会同样被关闭:

1
2
3
4
5
$ A="B
> C
> "
$ echo $A
B C

然而,由于 echo $A 时的变量沒置于 soft quote 中,因此当变量替换完成后并作命令行重组时,enter 会被解释为 IFS ,而不是解释为 New Line 字符

同样的,用 escape 亦可关闭 CR 字符:

1
2
3
4
5
$ A=B\
> C\
>
$ echo $A
BC

上例中,第一个 enter 跟第二个 enter 均被 escape 字符关闭了,因此也不作为 CR 來处理,但第三个 enter 由于没有被转义,因此作为 CR 结束 command line

但由于 enter 鍵本身在 shell meta 中的特殊性,在 \ 后面,仅仅取消其 CR 功能,而不会保留其 IFS 功能

be yourself, everyone else is already taken.