shell十三问地址:http://bbs.chinaunix.net/thread-218853-1-1.html
一、为何叫做shell
硬件的驱动和文件的管理需要通过操作系统来处理,而对于linux操作系统来说,使用者没法直接操作kernel,而是通过kernel的外壳来操作,这个外壳就是shell。
shell最简单的定义就是命令解释器:将使用者的命令翻译给kernel处理;同时将kernel处理的结果翻译给使用者。
在linux的预设系统中,通常有几种不同的shell,保存在/etc/shells里;常见的shell分为两大主流:
sh:
burne shell(sh)
burne again shell(bash)
csh:
c shell(csh)
tc shell(tcsh)
korn shell(ksh)
二、shell prompt(PS1)与Carriage Return(CR)
在linux上,可见的提示符一般是两种:
$:给一般使用者使用
#:给root使用
用户在shell prompt命令出现之后才能输入命令行,直到碰到CR(就是由Enter键产生)字符为止;
shell会依据IFS将命令行所输入的文字拆解为字段,然后在针对特殊字符先做处理,最后再重组整行。
IFS:shell预设使用的字段分隔符,可以由一个或者多个如下按键组成:
空格键
制表符
回车键
三、echo相关
-n选项:取消换行符
-e:启用反斜线转义字符
-E:关闭反斜线转义字符
四、双引号”“与单引号‘’的区别
shell将命令行的每一个字符分为两种:
(1)literal:普通的纯文字
(2)meta:具有特定功能的特殊保留字符
常见的meta:
IFS:空格、制表符、回车键
CR:由回车键产生
=:设定变量
$:做变量或运算替换
>:重定向stdout
<:重定向stdin
|:管道
&:重定向文件描述符,或将命令在后台执行
():将期内的命令至于nested subshell执行,或用于运算或命令替换
{}:将期内的命令置于non-named function中执行,或用在变量替换的界定范围
;:在前一个命令结束时,忽略其返回值,继续执行下一个命令
&&:在前一个命令结束时,若返回值为true,继续执行下一个命令。
||:在前一个命令结束时,若其返回值为false,继续执行下一个命令
!:执行history列表中的命令
假如在命令行中将这些保留字符的功能关闭的话,就需要quoting处理了:
在bash中,常用的quoting有如下三种方法:
hard quote:‘’单引号,凡在hard quote中的所有meta均被关闭。
soft quote:”“双引号,在soft quote中大部分meta都会被关闭,但某些则被保留,如$,/,单引号
escape:\反斜线,只有紧接在escape(跳脱字符)之后的单一meta才被关闭
或者说,单引号就是指将保留字符全部关闭,单引号中的所有字符均会原样输出;双引号的某些特殊字符会被引用,输出的是经过转移之后。
a='b c'
echo ‘$a'
$a
echo "$a"
b c
hangma@ubuntu:/home$ echo "sjf'$a'"
sjf'b c'
上面的例子在双引号中引用单引号会保留单引号,但是单引号中的内容依然会进行转义
hangma@ubuntu:/home$ echo '$adksjfkds"skdjfkdsj$a"'
$adksjfkds"skdjfkdsj$a"
上面的例子在单引号中引用双引号,但是双引号中的内容都不会进行转义
五、变量相关
1、变量就是利用一个特定的名称来存储一段可以变化的值
设定变量的规则:
(1)可以利用”=“来设定或重新定义变量的内容
(2)等号左右两边不能使用区隔符号(IFS),也应避免使用shell的保留字符
(3)变量名称不能使用$符号
(4)变量名称的第一个字母不能是数字
(5)变量名称长度不能超过256个字母
(6)变量名称及变量值大小写是有区别的
所谓的设定变量,就是用一个名称储存一个数值;
2、export
严格说来,在当前shell中所定义的变量都属于”本地变量“,只有经过export命令的”输出“处理,才能成为环境变量。
取消变量:unset名里那个
区别:
A=
unset A
第一行是将变量A设定为空值(NULL value),但第二行是让变量A不再存在。
比较这两者的不同:
(1)如果是unset A
var=${A=expr}
则结果是:
var=expr
A=expr
(2)如果是A=
var=${A=expr}
则结果是:
var=
A=
六、exec和source的差别:
1、所谓环境变量起始就是那些会传给子进程的变量;遗传性就是区分本地变量与环境变量的决定性指标;
环境变量只能从父进程到子进程单向继承;换句话说,在子进程中的环境如何变更,均不会影响父进程的环境。
2、正常来说,当我们执行一个shell script时,其实是先产生一个sub-shell的子进程,然后sub-shell再去产生命令行的子进程。
为什么 cd /etc/aa/bb/cc写入shell脚本时,shell不执行?
因为,一般shell 脚本使用subshell去执行的,从进程的观念来看,是父进程产生一个子进程去执行,当子进程结束后,会返回父进程,但是父进程的环境不会因为子进程的改变而改变,所谓的环境变量数目很多,其中工作目录($PWD)正是问题所在,当subshell执行脚本时,subshell的$PWD会因为cd而变更,但当返回父进程shell时,$PWD是不会变更的。
所谓source就是让script在当前的shell内执行,而不是产生一个subshell来执行;由于所有执行结果均于当前shell内完成,若script的环境有所改变,当然也会改变当前环境;
因此,只要我们要将原本单独输入的script命令行编程source命令的参数,就可轻易解决前面的问题。
exec也是让脚本在同一个进程执行,但是原有进程则被结束了;
原有进程是否终止,就是exec与source/fork的最大差异
七、()与{}差别
1、有时候,需要在一定条件下一次执行多个命令;
()与{}这两对符号,虽然两者都可将多个命令做群组处理,但是技术细节是不一样的:
( ):将包含的命令置于sub-shell去执行,也成为nested sub-shell
{ }:则是在同一个shell内完成,也成为non-named command group
要是在command group中扯上变量及其他环境的修改,就可以根据不同的需求来使用()或者{}
通常,若所做的修改时临时的,且不想影响原有或以后的设定,那就是用();反之,则使用{}
八、$(())与$()还有${}差别在哪
1、$()与``(反引号)都是用来做命令替换的,所谓命令替换就是完成引号里的命令行,然后将其结果替换出来,再重组命令行
hangma@ubuntu:/home$ echo the process work dir is $(echo $PWD)
the process work dir is /home
hangma@ubuntu:/home$ echo the last sunday is $(date -d "last sunday" +%Y-%m-%d)
the last sunday is 2013-08-18
在多层次的复合替换中,``需要额外的转义(\`)处理,而$()则比较直观:
command1 `command2`command3``
原本的意图是要先将command3替换出来给command2处理,再将结果传给command1`command2...`来处理
而,真正的结果是在命令行中确实分成了`command2`与``两段;
正确的输入应该是:
command1`command2\`command3\``
但是换成$()就没问题了:
command1$(command2$(command3))
2、${}其实就是用来做变量替换用的;
一般情况下,$var与${var}并没有啥不一样的,但是用${}会比精确的界定变量名称的范围:
hangma@ubuntu:/home$ a=b
hangma@ubuntu:/home$ echo $ab
hangma@ubuntu:/home$ echo ${a}b
bb
${}可以用来分别替换获得不同的值:
假定URL="www.google.cn"
echo ${URL%.*} 移除.*所匹配的最右边的内容
www.google
echo ${URL%%.*} 将从最右边开始一直匹配到最左边的.*移除(贪婪操作符)
www
echo ${URL#*.} 移除*.所匹配的最左边的内容
google.cn
echo ${URL##*.} 将从最左边开始一直匹配到最右边的*.移除(贪婪操作符)
cn
hangma@ubuntu:/home$ URL="www.google.com"
hangma@ubuntu:/home$ echo ${URL%.*}
www.google
hangma@ubuntu:/home$ echo ${URL%%.*}
www
hangma@ubuntu:/home$ echo ${URL%o*}
www.google.c
hangma@ubuntu:/home$ echo ${URL%%o*}
www.g
hangma@ubuntu:/home$ echo ${URL#o*}
www.google.com
hangma@ubuntu:/home$ echo ${URL#*o}
ogle.com
hangma@ubuntu:/home$ echo ${URL##*o}
m
先设定file变量如下:
hangma@ubuntu:/home$ file=/path1/dir2/dir3/myfile.txt
${file:0:5} 提取最左边的5个字节
${file:5:5} 提取第5个字节右边的连续5个字节
hangma@ubuntu:/home$ echo ${file:0:5} 提取最左边的5个字节
/path
hangma@ubuntu:/home$ echo ${file:5:5} 提取第5个字节右边的连续5个字节
1/dir
${file/dir/path} 将第一个dir替换为path
${file//dir/path} 将全部dir替换为path
hangma@ubuntu:/home$ echo ${file/dir/path}
/path1/path2/dir3/myfile.txt
hangma@ubuntu:/home$ echo ${file//dir/path}
/path1/path2/path3/myfile.txt
${#var}:计算变量var的长度
3、数组定义
A="DJFKSFJ dsjfkjd“是定义A为一个字符串
若是想定义一个数组A,则需要:
A=(s d g k l l h j m d)
例子
hangma@ubuntu:/home$ a={1 2 3 4 5 6} 如果使用花括号定义数组就是错误的
2: command not found
hangma@ubuntu:/home$ a=(1 2 3 4 5 6)
hangma@ubuntu:/home$ a=1
hangma@ubuntu:/home$ echo $a[1] 如果不适用花括号界定变量界限,就会输出错误
1[1]
hangma@ubuntu:/home$ echo $a
1
hangma@ubuntu:/home$ echo ${a[1]}
2
hangma@ubuntu:/home$ echo ${a[@]}
1 2 3 4 5 6
hangma@ubuntu:/home$ echo ${a[*]}
1 2 3 4 5 6
${a[@]}:输出数组a的所有变量
${a[*]}:输出数组a的所有变量
${#a[@]}:输出数组a的长度
${#a[1]}:输出a[1]的长度
hangma@ubuntu:/home$ a=(sjdkfljdsk sjdkfj sjdkfjdskl sjdkjfl)
hangma@ubuntu:/home$ echo ${#a[@]}
4
hangma@ubuntu:/home$ echo ${#a[1]}
6
4、$(()):用途是用来做整数运算的
符号有:+ - * / % & | ^ !
九、$@与$*差别在哪
$0代表脚本名称本身,而$1代表其后的第一个参数,$2,$3依次类推
$#代表脚本参数的个数,但是不包括脚本名称本身,就是说如果你没有给脚本传递参数,那么$#就是0
$# 与 $*的区别
两者只有在soft quote中才有差异,否则,都表示全部参数(不包括脚本名称本身)
”$@“扩展成”$1" "$2" "$3"
"$*"扩展成 “$1c$2c$3",其中c是IFS的第一个字符
"$@”用的最多,因为“$*"将所有字符串扩展成单个字符串,所以很少用。
#!/bin/bash
my_fun(){
echo "$#"
}
echo the num of para '"$@"' is $(my_fun "$@")
echo the num of para '"$*"' is $(my_fun "$*")
hangma@ubuntu:/home/ddddd/shelltest$ ./para.sh p1 "p2 p3" p4
the num of para "$@" is 3
the num of para "$*" is 1
十、&& 与 ||
1、test的表示式称为expression,其命令格式有两种:
test expression
或者
[ expression ]
请务必注意[]之间的空格键
test目前支持的测试对象只有三种:
string:字符串,也就是纯文字
integer:整数(0或者正整数、不含负数或小数点)
file:文件
2、字符串比较:
字符串比较最好用双中括号,因为有时候采用单中括号会产生错误,所以最好避开他们:
[[ $str1 == $str2 ]]:当str1等于str2,返回真
[[ $str1 != $str2 ]]:如果不相同,返回真
[[ $str1 > $str2 ]]:当str1大于str2,返回真
[[ $str1 < $str2 ]]:当str1小于str2,返回真
[[ -z $str ]]:当str包含的是空字符串,返回真
[[ -n $str ]]:当str包含的不是是空字符串,返回真
3、文件测试
(1)文件或者目录是否存在的判断
[ -f $var ]:如果变量包含正常的文件路径或文件名,则返回真
[ -d $var ]:如果变量包含的是目录,则返回真
[ -e $var ]:如果变量包含的文件存在,则返回真
(2)文件读写执行操作权限的判断
[ -r $var ]:如果变量包含的文件可以读,则返回真
[ -w $var ]:如果变量包含的文件可以写,则返回真
[ -x $var ]:如果变量包含的文件可以执行,则返回真
(3)文件类型判断
[ -c $var ]:如果变量包含的是一个字符设备文件的路径,则返回真
[ -b $var ]:如果变量包含的是一个块设备文件的路径,则返回真
[ -L $var ]:如果变量包含的是一个符号链接,则返回真
4、算术比较
-gt:大于
-lt:小于
-ge:大于或等于
-le:小于或等于
注:假如在test中碰到变量替换,使用双引号引用是最保险的。
5、&& 和||是用来组建多个command line用的:
command1 && command2:command2只有在command1为真时,才执行
command1 || command2:command2只有在command1为假时,才执行
十一、
1、*:匹配0个或者多个字符
?:匹配任意单一字符
[list]:匹配list中的任意单一字符
[!list]:匹配不在list中的任意单一字符
{str1,str2,str3...............}:匹配str1、str2、str3、、、、其一字符串
注:list可以为指定的个别字符,如abcd;也可以为一段ASCII字符的起止范围,如a-d
2、a*b:a与b之间可以有任意长度的字符,也可以一个没有
a?b:a与b之间必须也只能有一个字符,可以是任意字符,如:aab,abb,acb,a0b等
a[xyz]:a与b之间必须也只能有一个字符,但只能是x或y或z,如:axb,ayb,azb这三个
a[!0-9]:a与b之间必须也只能有一个字符,但不能是阿拉伯数字,如axb,aab,a-b等
a{abc,xyz,123}b:a与b之间只能是abc或xyz或123这三个字符串之一,如aabcb,axyzb,a123b这三个
注:(1)[!]中的!只有放在第一顺位时,才有排除之功,举例说:[!a]*表示当前目录下所有不以a开首的路径名称
/tmp/[a\!]*表示/tmp目录下以a或!开首的路径名称
(2)[ - ]中的-左右两边均有字符时,才表示一段范围,否则仅作“-”字符处理
/tmp/*[-z]/[a-zA-Z]*表示/tmp目录下所有以z或-结尾的子目录下以英文字母(不分大小写)开首的路径名称
字符组合:char set
所谓的字符组合就是将多个连续的字符做一个集合:
abc:表示abc三个连续的字符,但彼此独立而非集合,可视为三个字符组合
(abc):表示abc这三个连续字符的集合,可视为一个字符组合
abc|xyz:表示abc或者xyz这两个字符组合之一
[abc]:表示单一字符,可为a或b或c
[^abc]:表示单一字符,不为a或b或c即可
锚点:用以标识句子中的位置所在:
^:表示句首,例如^abc表示以abc开首的句子
$:表示句尾,如abc$表示以abc为结尾的句子
\<:表示词首,\ \>:表示词尾,如abc\>表示以abc结尾的词 修饰符: *:表示出现0到多个 {n}:表示前一个字符组合出现次数必须为n次 {n,}:表示前一个字符组合出现次数至少为n次 {n,m}表示a与c之间有n到m个存在