[转帖]""(双引号) 与''(单引号) 差在哪?
""(双引号) 与''(单引号) 差在哪?
- http://wiki.jikexueyuan.com/project/13-questions-of-shell/double-single.html
- hard quote
- 还有
- soft quote
还是回到我们的command line
来吧...
经过前面两章的学习,应该很清楚当你在shell prompt
后面敲打键盘, 直到按下Enter
键的时候,你输入的文字就是command line
了, 然后shell
才会以进程的方式执行你所交给它的命令。 但是,你又可知道:你在command line
中输入的每一个文字, 对shell
来说,是有类别之分的呢?
简单而言,(我不敢说精确的定义,注 1),command line
的每一个charactor
, 分为如下两种:
- literal:也就是普通的纯文字,对
shell
来说没特殊功能; - meta: 对
shell
来说,具有特定功能的特殊保留元字符。
Note
对于
bash shell
在处理comamnd line
的顺序说明, 请参考 O'Reilly 出版社的 Learning the Bash Shell,2nd Edition, 第 177-180 页的说明,尤其是 178 页的流程图:Figure 7-1 ...
literal
没什么好谈的, 像 abcd、123456 这些 "文字" 都是 literal...(so easy? ^_^) 但 meta 却常使我们困惑...(confused?) 事实上,前两章,我们在command line
中已碰到两个 似乎每次都会碰到的 meta:
IFS
:有space
或者tab
或者Enter
三者之一组成 (我们常用 space)CR
: 由Enter
产生;
IFS
是用来拆解command line
中每一个词 (word) 用的, 因为shell command line
是按词来处理的。 而CR
则是用来结束command line
用的,这也是为何我们敲Enter
键, 命令就会跑的原因。
除了常用的IFS
与CR
, 常用的 meta 还有:
meta 字符 | meta 字符作用 |
---|---|
= | 设定变量 |
$ | 作变量或运算替换 (请不要与shell prompt 混淆) |
> | 输出重定向 (重定向 stdout) |
< | 输入重定向 (重定向 stdin) |
| | 命令管道 |
& | 重定向 file descriptor 或将命令至于后台 (bg) 运行 |
() | 将其内部的命令置于 nested subshell 执行,或用于运算或变量替换 |
{} | 将期内的命令置于 non-named function 中执行,或用在变量替换的界定范围 |
; | 在前一个命令执行结束时,而忽略其返回值,继续执行下一个命令 |
&& | 在前一个命令执行结束时,若返回值为 true,继续执行下一个命令 |
|| | 在前一个命令执行结束时,若返回值为 false,继续执行下一个命令 |
! | 执行 histroy 列表中的命令 |
... | ... |
假如我们需要在command line
中将这些保留元字符的功能关闭的话, 就需要 quoting 处理了。
在bash
中,常用的 quoting 有以下三种方法:
- hard quote:''(单引号),凡在 hard quote 中的所有 meta 均被关闭;
- soft quote:""(双引号),凡在 soft quote 中大部分 meta 都会被关闭,但某些会保留 (如 $);
- escape: \ (反斜杠),只有在紧接在 escape(跳脱字符) 之后的单一 meta 才被关闭;
Note:
在 soft quote 中被豁免的具体 meta 清单,我不完全知道, 有待大家补充,或通过实践来发现并理解。
下面的例子将有助于我们对 quoting 的了解:
$ A=B C #空白符未被关闭,作为IFS处理
$ C:command not found.
$ 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 键字符亦然:
$ A=``B
> C
> '
$ echo "$A"
B
C
在上例中,由于enter
被置于 hard quote 当中,因此不再作为CR
字符来处理。 这里的enter
单纯只是一个断行符号 (new-line) 而已, 由于command line
并没得到CR
字符, 因此进入第二个shell prompt
(PS2
,以 > 符号表示), command line
并不会结束,直到第三行, 我们输入的enter
并不在 hard quote 里面, 因此没有被关闭, 此时,command line
碰到CR
字符,于是结束,交给 shell 来处理。
上例的Enter
要是被置于 soft quote 中的话,CR
字符也会同样被关闭:
$ A="B
> C
> "
$ echo $A
B C
然而,由于 echo $A
时的变量没有置于 soft quote 中, 因此,当变量替换完成后,并作命令行重组时,enter
被解释为IFS
, 而不是 new-line 字符。
同样的,用 escape 亦可关闭 CR 字符:
$ A=B\
> C\
>
$ echo $A
BC
上例中的,第一个enter
跟第二个enter
均被 escape 字符关闭了, 因此也不作为CR来处理,但第三个enter
由于没有被 escape, 因此,作为CR
结束command line
。 但由于enter
键本身在 shell meta 中特殊性,在 \ escape 字符后面 仅仅取消其CR
功能, 而不保留其 IFS 功能。
你或许发现光是一个enter
键所产生的字符,就有可能是如下这些可能:
- CR
- IFS
- NL(New Line)
- FF(Form Feed)
- NULL
- ...
至于,什么时候解释为什么字符,这个我就没法去挖掘了, 或者留给读者君自行慢慢摸索了...^-^
至于 soft quote 跟 hard quote 的不同,主要是对于某些 meta 的关闭与否,以 $ 来做说明:
$ A=B\ C
$ echo "$A"
B C
$ echo '$A'
$A
在第一个echo
命令行中,$ 被置于 soft quote 中,将不被关闭, 因此继续处理变量替换, 因此,echo
将 A 的变量值输出到屏幕,也就是 "B C" 的结果。
在第二个echo
命令行中,被置于hardquote中,则被关闭,因此, 只是一个 符号,并不会用来做变量替换处理,因此结果是 符号后面接一个 A 字母:$A。
练习与思考: 如下结果为何不同?
tips: 单引号和双引号,在 quoting 中均被关闭了。
$ A=B\ C
$ echo '"$A"' #最外面的是单引号
"$A"
$ echo "'$A'" #最外面的是双引号
'B C'
在 CU 的 shell 版里,我发现很多初学者的问题, 都与 quoting 的理解有关。 比方说,若我们在 awk 或 sed 的命令参数中, 调用之前设定的一些变量时,常会问及为何不能的问题。
要解决这些问题,关键点就是:区分出 shell meta 与 command meta
前面我们提到的那些 meta,都是在 command line 中有特殊用途的, 比方说 {} 就是将一系列的 command line 置于不具名的函数中执行 (可简单视为 command block), 但是,awk 却需要用 {} 来区分出 awk 的命令区段 (BEGIN,MAIN,END). 若你在 command line 中如此输入:
$ awk {print $0} 1.txt
由于 {} 在 shell 中并没有关闭,那 shell 就将 {print $0} 视为 command block, 但同时没有;
符号作命令分隔,因此,就出现 awk 语法错误结果。
要解决之,可用 hard quote:
awk '{print $0}'
上面的 hard quote 应好理解,就是将原来的 {、、$、} 这几个 shell meta 关闭, 避免掉在 shell 中遭到处理,而完整的成为 awk 的参数中 command meta。
Note:
awk 中使用的 0是awk中内建的fieldnubmer,而非awk的变量,awk自身的变量无需使用。
要是理解了 hard quote 的功能,在来理解 soft quote 与 escape 就不难:
awk "{print \$0}" 1.txt
awk \{print \$0\} 1.txt
然而,若要你改变 awk 的 0的0值是从另一个shell变量中读进呢?比方说:已有变量A 的值是 0, 那如何在command line
中解决 awk 的 $$A 呢? 你可以很直接否定掉 hard quote 的方案:
$ awk '{print $$A}' 1.txt
那是因为 A的 在 hard quote 中是不能替换变量的。
聪明的读者 (如你!),经过本章的学习,我想,你应该可以理解为 为何我们可以使用如下操作了吧:
A=0
awk "{print \$$A}" 1.txt
awk \{print\ \$$A\} 1.txt
awk '{print $'$A'}' 1.txt
awk '{print $'"$A"'}' 1.txt
或许,你能给出更多方案... ^_^
更多练习:
- http://bbs.chinaunix.net/forum/viewtopic.php?t=207178 一个关于 read 命令的小问题: 很早以前觉得很奇怪:执行 read 命令,然后读取用户输入给变量赋值, 但如果输入是以空格键开始的话,这空格会被忽略,比如:
read a #输入: abc
echo "$a" #只输出abc
原因: 变量 a 的值,从终端输入的值是以 IFS 开头,而这些 IFS 将被 shell 解释器忽略 (trim)。 应该与 shell 解释器分词的规则有关;
read a #输入:\ \ \ abc
echo "$a" #只输出abc
需要将空格字符转义
Note:
IFS Internal field separators, normally space, tab, and newline (see Blank Interpretation section). ...... Blank Interpretation After parameter and command substitution, the results of substitution
are scanned for internal field separator characters (those found in IFS) and split into distinct arguments where such characters are found. Explicit null arguments (""or'') are retained.
Implicit null arguments(those resulting from parameters that have no values) are removed. (refre to: man sh)
解决思路:
- shell command line 主要是将整行 line 给分解 (break down) 为每一个单词 (word);
- 而词与词之间的分隔符就是 IFS (Internal Field Seperator)。
- shell 会对 command line 作处理 (如替换,quoting 等), 然后再按词重组。(注:别忘了这个重组特性)
- 当你用 IFS 来事开头一个变量值,那 shell 会先整理出这个词,然后在重组 command line。
- 然而,你将 IFS 换成其他,那 shell 将视你哪些 space/tab 为 “词”,而不是 IFS。那在重组时,可以得到这些词。
若你还是不理解,那来验证一下下面这个例子:
$ A=" abc"
$ echo $A
abc
$ echo "$A" #note1
abc
$ old_IFS=$IFS
$ IFS=;
$ echo $A
abc
$ IFS=$old_IFS
$ echo $A
abc
Note:
这里是用 soft quoting 将里面的 space 关闭,使之不是 meta(IFS), 而是一个 literal(white space);
- IFS=; 意义是将 IFS 设置为空字符,因为; 是 shell 的元字符 (meta);
问题二:为什么多做了几个分号,我想知道为什么会出现空格呢?
$ a=";;;test"
$ IFS=";"
$ echo $a
test
$ a=" test"
$ echo $a
test
$ IFS=" "
$ echo $a
test
解答:
这个问题,出在IFS=;
上。 因为这个;
在问题一中的 command line 上是一个 meta, 并非";"
符号本身。 因此,IFS=;
是将 IFS 设置为 null charactor (不是 space、tab、newline)。
要不是试试下面这个代码片段:
$ old_IFS=$IFS
$ read A
;a;b;c
$ echo $A
;a;b;c
$ IFS=";" #Note2
$ echo $A
a b c
Note:
要关闭
;
可用";"
或者';'
或者\;
。
思考问题二:文本处理:读文件时,如何保证原汁原味。
cat file | while read i
do
echo $i
done
文件 file 的行中包含若干空,经过 read 只保留不重复的空格。 如何才能所见即所得。
cat file | while read i
do
echo "X${i}X"
done
从上面的输出,可以看出 read,读入是按整行读入的; 不能原汁原味的原因:
- 如果行的起始部分有 IFS 之类的字符,将被忽略;
echo $i
的解析过程中,首先将 $i 替换为字符串, 然后对 echo 字符串中字符串分词,然后命令重组,输出结果; 在分词,与命令重组时,可能导致多个相邻的 IFS 转化为一个;
cat file | while read i
do
echo "$i"
done
以上代码可以解决原因 2 中的,command line 的分词和重组导致 meta 字符丢失; 但仍然解决不了原因 1 中,read 读取行时,忽略行起始的 IFS meta 字符。
回过头来看上面这个问题:为何要原汁原味呢? cat 命令就是原汁原味的,只是 shell 的 read、echo 导致了某些 shell 的 meta 字符丢失;
如果只是 IFS meta 的丢失,可以采用如下方式: 将 IFS 设置为 null,即IFS=;
, 在此再次重申此处;
是 shell 的 meta 字符, 而不是 literal 字符; 因此要使用 literal 的 ;
应该是\;
或者关闭 meta 的 (soft/hard) quoting 的";"
或者';'
。
因此上述的解决方案是:
old_IFS=$IFS
IFS=; #将IFS设置为null
cat file | while read i
do
echo "$i"
done
IFS=old_IFS #恢复IFS的原始值
现在,回过头来看这个问题,为什么会有这个问题呢; 其本源的问题应该是没有找到解决原始问题的最合适的方法, 而是采取了一个迂回的方式来解决了问题;
因此,我们应该回到问题的本源,重新审视一下,问题的本质。 如果要精准的获取文件的内容,应该使用 od 或者 hexdump 会更好些。
[转帖]""(双引号) 与''(单引号) 差在哪?的更多相关文章
- " "( 双引号) 与 ' '( 单引号) 差在哪?-- Shell十三问<第四问>
" "( 双引号) 与 ' '( 单引号) 差在哪?-- Shell十三问<第四问> 经过前面两章的学习,应该很清楚当你在 shell prompt 后面敲打键盘.直到 ...
- Struts2 用 s:if test 判断属性和字符串相等时 注意双引号和单引号的使用
字符串N一定要用“”双引号包含,从test的包含则用单引号 ‘ ’,如果相反,则不能正确判断该属性是否与该字符串相等. 正确:<s:if test='activityBean.searchFor ...
- Excel公式中双引号和单引号输入和显示以及函数的选择确认
[Excel中显示双引号] 1.直接输入双引号“”或单引号“ 2.工式中显示双引号需输入“”“”“”(六个引号)或单引号需输入“”“”(四个引号) [Excel中快速确认已选择的函数] 1.用键盘的上 ...
- onmousemove和onmouseout事件的调用,和js使用双引号、单引号的时候应该注意的问题
使用js的时候,统一使用双引号,然后通过反斜杠进行转义 ①如果同时使用单引号.和双引号的情况下容易出现问题,导致标签中表示的事件不能调用, ②导致由于标签没有封口而出现样式布局错误 <!DOCT ...
- php中的双引号和单引号的区别?
1.单引号里面的字符串直接全部转义,原样输出(即:单引号内部的变量不会被执行) 2.双引号里面的变量会被替换(即:变量会执行) 例如:$name = 'hello';echo "the $n ...
- shell中定义变量用双引号和单引号以及不用引号的区别
1. 单引号 使用单引号的情况下,不管里面的是否有变量或者其他的表达是都是原样子输出 2. 双引号 如果其定义变量的时候使用双引号的话,则里面的变量或者函数会通过解析,解析完成后再输出内容,而不是把双 ...
- PHP中双引号和单引号的区别
在PHP中,字符串数值有单引号和双引号两种. 区别: 单引号:系统不做复杂的转义.只转义\'和\\两种转义,其他的按原样输出. 双引号:则转义比较多,\",\\,\r,\t,\n,\$等. ...
- PHP:php中的双引号和单引号的区别
双引号: $a="369"; $b="$a"; echo $b;//输出:369 单引号: $a="369"; $b='$a'; echo ...
- vue中将分号去掉,将双引号变为单引号的配置
在项目根目录下创建.prettierrc文件,文件内容如下: { "semi": false, "singleQuote": true } 实现vs code中 ...
随机推荐
- 强化学习(九)Deep Q-Learning进阶之Nature DQN
在强化学习(八)价值函数的近似表示与Deep Q-Learning中,我们讲到了Deep Q-Learning(NIPS 2013)的算法和代码,在这个算法基础上,有很多Deep Q-Learning ...
- Java 集合详解
一.集合的由来 通常,我们的程序需要根据程序运行时才知道创建多少个对象.但若非程序运行,程序开发阶段,我们根本不知道到底需要多少个数量的对象,甚至不知道它的准确类型.为了满足这些常规的编程需要,我们要 ...
- 基于Log4Net本地日志服务简单实现
背景 项目开发中,我们或多或少会使用诸如NLog,Log4Net,Kafka+ELK等等日志套件: 基于关注点分离原则,业务开发的时候不应该关注日志具体实现:并且后续能方便切换其他日志套件: 这里先实 ...
- 4.2WebHost配置「深入浅出ASP.NET Core系列」
希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,谢谢关注. WebHost配置 覆盖配置文件和修改启动URL 覆盖配置文件和修改启动URL是经常使用的地方,覆盖配置文件可以自 ...
- Writing a Simple Service and Client (C++)
此前说的publisher/subscriber都是广播式的,subscriber被动地接收消息,二者没有request/response这种交互. Service Node Client Node ...
- AngularJs with Webpackv1 升級到 Webpack4
本篇記錄一下升級的血淚過程 請注意升級前請先創一個新目錄將升級應用與舊應用隔離 1. 需要將相關的套件做統一升級的動作,已確認需要升級所有舊的loaders 其它應用的套件可先不做升級的動作 (如果編 ...
- javascript小记四则:用JS写一个滚动横条文字,可以根据需要进行修改;
网页上的一些广告文字,一直会滚动是怎么做到的,今天给大家演示下,非常简单,源码如下(本案例是在.net平台上,但HTML是通用的): <!DOCTYPE html> <html> ...
- Java 加密、解密PDF文档
本篇文章将介绍通过Java编程来设置PDF文档保护的方法.我们可以设置仅用于查阅文档的密码,即该通过该密码打开文档仅用于文档阅读,无法编辑:也可以设置文档编辑权限的密码,即通过该密码打开文档时,文档为 ...
- Arrays工具、二维数组以及LeetCode练习题
1 Arrays PS:Arrays位于java.util包下 int binarySearch(type[] a, type key); 使用二分法查询 key 元素在 a 数组中的索引,如果数组不 ...
- VSCode瞎折腾记
搬到小机房后终于能用VSCode啦(没错以前的系统是xp) 但是这东西比Dev难搞多了qwq,简单记一下自己的DIY历程吧(不然全搞炸就凉了) 设置语言为中文 可以直接下载插件 让VSCode支持编译 ...