1、shell 会依据 IFS(Internal Field Seperator) 将 command line 所输入的文字给拆解为"字段"(word)。然后再针对特殊字符(meta)先作处理,最后再重组整行 command line。
其中的 IFS 是 shell 预设使用的字段分隔符,可以由一个及多个如下按键组成: * 空格键(White Space)
* 表格键(Tab)
* 回车键(Enter)
系统可接受的命令名称(command-name)可以从如下途径获得:
* 明确路径所指定的外部命令
* 命令别名(alias)
* 自定功能(function)
* shell 内建命令(built-in)
* $PATH 之下的外部命令
2、" "(双引号) 与 ' '(单引号)差在哪?
command line 的每一个 charactor ,分为如下两种:
* literal:也就是普通纯文字,对 shell 来说没特殊功能。
* meta:对 shell 来说,具有特定功能的特殊保留字符。
假如我们需要在 command line 中将这些保留字符的功能关闭的话,就需要 quoting 处理了。
在 bash 中,常用的 quoting 有如下三种方法:
* hard quote:' ' (单引号),凡在 hard quote 中的所有 meta 均被关闭。
* soft quote: " " (双引号),在 soft quoe 中大部份 meta 都会被关闭,但某些则保留(如 $ )。
* escape : \ (反斜线),只有紧接在 escape (跳脱字符)之后的单一 meta 才被关闭。
3、ENTER键不等于CR
代码: $ 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 来处理。 同样的道理反斜线也会起到同样的作用:
代码: $ A=B\ > C\ > $ echo $A B C |
4、在 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}' 1.txt |
上面的 hard quote 应好理解,就是将原本的{、<space>、$(注三)、}这几个shell meta 关闭, 避免掉在 shell 中遭到处理,而完整的成为 awk 参数中的 command meta。(注三:而其中的 $0 是 awk 内建的 field number,而非 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 呢?
代码: 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 中 |
4、变量替换与command line
代码: $ A=ls $ B=la $ C=/tmp $ $A -$B $C |
必需强调的是,我们所提的变量替换,只发生在 command line 上面。(是的,让我们再回到 command line 吧﹗) 仔细分析最后那行 command line ,不难发现在被执行之前(在输入 CR 字符之前),
$ 符号会对每一个变量作替换处理(将变量值替换出来再重组命令行),最后会得出如下命令行:
代码: ls -la /tmp |
若从技术细节来看,shell 会依据 IFS(Internal Field Seperator) 将 command line 所输入的文字给拆解为"字段"(word)。 然后再针对特殊字符(meta)先作处理,最后再重组整行 command line。 这里的 $ 就是 command line 中最经典的 meta 之一了,就是作变量替换的。
5、exec跟source 差在哪?
提问:
|
让我们回到上一章所谈到的"环境变量"吧:
* 所谓环境变量其实就是那些会传给子进程的变量。简单而言,"遗传性"就是区分本地变量与环境变量的决定性指针。
然而,从遗传的角度来看,我们也不难发现环境变量的另一个重要特征:
* 环境变量只能从父行程到子行程单向继承。换句话说:在子行程中的环境如何变更,均不会影响父行程的环境。
正常来说,当我们执行一个 shell script 时,其实是先产生一个sub-shell的子行程,然后sub-shell 再去产生命令行的子行程。因为,一般我们跑的 shell script 是用subshell去执行的。
从 process 的观念来看,是 parent process 产生一个 child process 去执行,当 child 结束后,会返回 parent ,但 parent 的环境是不会因 child 的改变而改变的。所谓的环境元数很多,凡举 effective id, variable, workding dir 等等...
* 所谓 source 就是让 script 在当前 shell 内执行、而不是产生一个 sub-shell 来执行。 由于所有执行结果均于当前 shell 内完成,若 script 的环境有所改变,当然也会改变当前环境了。
因此,只要我们要将原本单独输入的 script 命令行变成 source 命令的参数,就可轻易解决前例提到的问题了。
代码: source ./my.script 或: . ./my.script |
* exec 也是让 script 在同一个行程上执行,但是原有行程则被结束了。
也就是简而言之:原有行程会否终止,就是 exec 与 source/fork 的最大差异了。
6、( )与{ }差在哪?
在 shell command line 中,一般人或许不太计较 ( ) 与 { } 这两对符号的差异,
虽然两者都可将多个命令作群组化处理,但若从技术细节上,却是很不一样的:
( ) 将 command group 置于 sub-shell 去执行,也称 nested sub-shell。
{ } 则是在同一个 shell 内完成,也称为 non-named command group。
要是在 command group 中扯上变量及其它环境的修改,我们可以根据不同的需求来使用 ( ) 或 { }。 通常而言,若所作的修改是临时的,且不想影响原有或以后的设定,那我们就 nested sub-shell,反之,则用 non-named command group 。
7、$(( )) 与 $( ) 还有${ } 差在哪?
在 bash shell 中,$( ) 与 ` ` (反引号) 都是用来做命令替换用(command substitution)的。所谓的命令替换与我们第五章学过的变量替换差不多,都是用来重组命令行:
* 完成引号里的命令行,然后将其结果替换出来,再重组命令行。
接下来,再让我们看 ${ } 吧... 它其实就是用来作变量替换用的啦。 一般情况下,$var 与 ${var} 并没有啥不一样。但是用 ${ } 会比较精确的界定变量名称的范围,比方说:
代码: $ A=B $ echo $AB |
可以用 ${ }分别替换获得不同的值:
代码: ${file#*/}:拿掉第一条 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
${file:0:5}:提取最左边的 5 个字节:/dir1
|
也可以对变量值里的字符串作替换:
代码:
${file/dir/path}:将第一个 dir 提换为 path:/path1/dir2/dir3/my.file.txt
${file//dir/path}:将全部 dir 提换为 path:/path1/path2/path3/my.file.txt
|
利用 ${ }还可针对不同的变量状态赋值(没设定、空值、非空值):
代码:
${file-my.file.txt} :假如 $file 为空值,则使用 my.file.txt 作默认值。(保留没设定及非空值)
|
还有哦,${#var} 可计算出变量值的长度:
代码:
${#file} 可得到 27 ,因为 /dir1/dir2/dir3/my.file.txt 刚好是 27 个字节...
|
最后为大家介绍 $(( )) 的用途吧:它是用来作整数运算的。
在 bash 中,$(( )) 的整数运算符号大致有这些:
+ - * / :分别为 "加、减、乘、除"。
% :余数运算
& | ^ !:分别为 "AND、OR、XOR、NOT" 运算。
8、[^ ]跟[! ]差在哪?
这道题目说穿了,就是要探讨Wildcard与Regular Expression的差别的。
让我 们来看看wildcard是什么回事吧。
Part-I:
Wildcard 首先, wildcard也是属于command line的处理工序,作用于argument里的path之上. 没错,它不用在command_name也不用在options上. 而且,若argument不是path的话,那也与wildcard无关. 换句更为精确的定义来讲, wildcard是一种命令行的路径扩展(path expansion)功能. 提到这个扩展,那就不要忘记了command line的"重组"特性了! 是的,这与变量替换(variable substitution)及命令替换(command
substitution)的重组特性是一样的! 也就是在wildcard进行扩展后,命令行会先完成重组才会交给shell来处理.
Part-II:
Regular Expression
9、eval的作用?
再一次替换重组
a=1
A1=abc
|
我们都知道echo $A1就可得到abc这个結果.
然而, 我们能否用$A$a來取代$A1而同样替换出abc呢?
这个问题我们可以轻松的用eval来解決:
eval echo \$A$a |