转字:http://lspgyy.blog.51cto.com/5264172/1282107
你在shell prompt(shell 提示)后面敲打键盘、直到按下enter的时候,你输入的文字就是command line(命令行)了,然后shell才会以进程方式执行你所提交的命令。但是,你又可知道:你在command line输入的每一个文字,对shell来说,有什么类别之分呢? 简单而言,command line的每一个charactor(字符)分为如下两种: *literal(文字):也就是普通纯文字,对shell来说没有特殊功能。 *meta(元字符):对shell来说,具有特定功能的保留字。 literal没有什么好说的,凡是 abcd、123456 等这些“文字”都是literal。但是meta却常使我们困惑。事实上,前两章我们在command line中已碰到两个几乎每次都会碰到的meta: *IFS(交换字段分隔符):由<space> <tab> <enter>三者之一组成(我们常用space)。 *CR(回车键):由<enter>产生。 IFS是用来拆分command line的每一个词(word)用的,因为shell command line是按词来处理的。而CR则是用来结束command line用的,这也是为何我们敲<enter>命令就会执行的原因。除了IFS和CR外,常用的meta还有: = : 设定变量。 $ : 做变量或运算替换(请不要与 shell prompt 搞混了)。 > : 重定向 stdout(标准输出standard out)。 < : 重定向 stdin(标准输入standard in)。 |: 管道命令。 & : 重定向 file descriptor (文件描述符),或将命令置于后台执行。 ( ): 將其內的命令置于 nested subshell (嵌套的子shell)执行,或用于运算或命令替换。 { }: 將其內的命令置于 non-named function(未命名函数) 中执行,或用在变量替换的界定范围。 ; : 在前一个命令结束时,而忽略其返回值,继续执行下一個命令。 && : 在前一個命令结束时,若返回值为 true,继续执行下一個命令。 || : 在前一個命令结束时,若返回值为 false,继续执行下一個命令。 !: 执行 history 列表中的命令 .... 假如我们要在command line中将这些保留元字符的功能关闭的话,就要用到 quoting (引用)处理了。 在bash中,我们常用的 quoting有如下三种方法: *hard quote:''(单引号),凡在hard quote中的所有meta均被关闭。 *soft quote:""(双引号),在soft quote中的大部分meta都会被关闭,但某些保留(如$)。 *escape:\ (反斜线),只有紧接在escape(跳脱字符)之后的单一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> 由于没有被跳脱,因此作为 CR 结束 command line 。 但由于 <enter> 鍵本身在 shell meta 中的特殊性,在 \ 跳脱后面,仅仅取消其 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 命令行中,$ 被置于 hard quote 中,则被关闭,因此 $ 只是一个 $ 符号, 并不会用來作变量替换处理,因此结果是 $ 符号后面接一个 A 字母:$A 。 --------------------------------------------------------- 练习与思考:以下两条命令输出的结果分别是什么? $ A=B\ C $ echo '"$A"' # 最外面的是单引号 $ echo "'$A'" # 最外面的是双引号 在 CU 的 shell 版里,我发现有很多初学者的问题,都与 quoting 理解的有关。 比方说,若我们在 awk 或 sed 的命令参数中调用之前设定的一些变量时,常会问及为何不能的问题。 要解决这些问题,关键点就是: * 区分出 shell meta 与 command meta 前面我们提到的那些 meta ,都是在 command line 中有特殊用途的, 比方说 { } 是將其內一系列 command line 置于不具名的程序中执行(可简单视为 command block ), 但是,awk 却需要用 { } 來区分出 awk 的命令区段(BEGIN, MAIN, END)。 若你在 command line 中如此输入:
但同时又沒有" ; "符号作命令区隔,因此就出现 awk 的语法错误結果。 要解決之,可用 hard quote :
避免掉在 shell 中遭到处理,而完整的成为 awk 参数中的 command meta 。 ( 注三:而其中的 $0 是 awk 內建的 field number ,而非 awk 的变量, awk 自身的变量无需使用 $ 。) 要是理解了 hard quote 的功能,再來理解 soft quote 与 escape 就不难:
比方说:已有变量 $A 的值是 0 ,那如何在 command line 中解決 awk 的 $$A 呢? 你可以很直接否定掉 hard quoe 的方案: $ 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 # 注:"$A" 包在 soft quote 中,,此处也要注意''与""的结合 单引号和双引号都能关闭shell对特殊字符的处理。不同的是,双引号没有单引号严格,单引号关闭所有有特殊作用的字符,而双引号只要求shell忽略大多数,具体的说,就是①美元符号②反引号③反斜杠,这3种特殊字符不被忽略。 不忽略美元符号意味着shell在双引号内部也进行变量名替换。 下面用一个简单的shell程序要说明一下。
这是正确的lu程序,下面是运行结果。
如果lu写成①grep $1 phonebook或者②grep '$1' phonebook,就会出现下面的错误结果(为什么?)。 ①的结果:
②的结果:
|
补充:
echo
puts a space between each two arguments. The shell considers the newline in $num
just a word separator (just like space).
lines="a
b
c"
set -x
echo $lines # several arguments to echo
echo "$lines" # one argument to echo
See this answer (by the OP himself) for a more detailed explanation.
Linux/UNIX Shell Scripting: Why does "echo" remove spaces and newlines sometimes? |
One common problem I have personally made and seen others make while shell scripting is trying to set a variable to be the contents of a file or the output of a multiline command, and then trying to echo the variable to process it further with grep, awk, a while loop, etc. Depending on how you do this you might get some unexpected results because you might have missing newlines and spaces from your output.
In this example, we have a file named "testfile" that contains 5 lines. I then set the testvar variable to contain the contents of the line by running "testvar=`cat testfile`". However, when I attempt to "echo $testvar" all the lines of the file are shown on one line! If I try to grep for "test line 2" it still see's everything on one line.
The same problem happens when trying to set testvar to be the output of "ls -al" and then echoing the variable:
So what is going on here? It all has to do with how command line parameters are parsed by the shell and provided to the echo command.
Here is an example that illustrates what is happening:
Notice how the echo command removed all the extra spaces out of what I had typed? It is doing this because the shell is parsing each word as an argument and providing the arguments to the echo command:
The shell is parsing arguments and providing them to the echo command. As part of parsing the arguments, all of the extra spaces are being removed by the shell before they are passed to the echo command. This is the same thing that was happening when our newlines where removed from the first examples. The shell was parsing everything as arguments and removing all extra new lines and spaces.
How to fix this? It is very easy... Just use double quotes so that a single parameter is passed to the echo command:
By putting quotes around the text, it causes the shell to pass the entire text within the quotes as a single argument to echo which preserves the spaces.
This same technique works when echoing a variable that contains the output of a command or the contents of a file:
Hopefully this post helped you understand how commands are parsed by the shell and why you can sometimes see echo commands unexpectedly removing spaces and newlines.