题=C"Linux Shell教程"BS30
本文使用仙符''进行排版。
Linux的Shell脚本是Linux的基础之一,学习Shell语法是Linux的必经之路。下面来讲Shell的语法。
脚本解释器,即shell的种类,有:bash,sh,ash,csh(tcsh),ksh. 注意,bash完全兼容sh,一般写sh类型的或bash类型的(後缀sh)。
开头"#!"後接shell执行环境,如:"#!/bin/sh","#!/bin/bash"
关于sh和bash的区别,简单讲即sh是bash --posix,详细的请自查。
"特别注意,阅读请设置tab长度为4字符宽度,中文为2字符宽度,推荐用notepad++查看以免制表符不对齐。"Cred
"一、shell变量"BS15
定义变量,使用赋值语句,如:variableName=value (等号两边没有空格)。
引用变量,用'$'引用变量,如:$variableName 或 ${variableName} (花括号限定边界)。
重定义变量,直接定义即可。取消变量定义,用unset,如:unset variableName (不能删除只读变量)
只读变量,可以将已定义变量改为只读,如:readonly variableName
显示所有本地shell变量,用set命令即可。
变量类型:1)局部变量,2)环境变量,3)shell特殊变量(命令行参数是特殊变量)
"命令行参数:"B
$0 当前脚本的文件名
$# 传递给脚本或函数的参数个数(不包括$0)。
$* 传递给脚本或函数的所有参数(不包括$0)。
$@ 传递给脚本或函数的所有参数(不包括$0)。被双引号包含时,与 $* 稍有不同,下面将会单独讲到。
$? 上个命令的退出状态,或函数的返回值。成功返回0,否则非零(一般为1)。
$$ 当前进程的ID
$n 传递给脚本或函数的第n个参数。例如,第一个参数是$1,第二个参数是$2
如果获取第十个及其後的命令行参数,用${n}的形式。
$* 和 $@ 的区别:
前者将参数当做一个整体,後者将参数作为个体连接。所以,$* 和 $@ 是一样的,而"$*" 和 "$@"不一样,有言:
"$*" is equivalent to "$1c$2c...", where c is the first character of the value of the IFS variable.
"$@" is equivalent to "$1" "$2" ...
IFS variable(内部分隔符),有'\040'(空格),'\011'(制表符),'\012'(换行符)。
${*}和${@}的变量替换:
此两者的区别在变量替换,在${ }中使用*和@是有区别的:
"使用@得到结果列表,使用*得到一个字符串"UBCred,如:
${@:0} 得到$0 $1 $2 ... 的结果列表
${*:0} 得到$0 $1 $2 ... 的单个字符串
注意,@和*在花括号里面进行某些高级替换(见字符串操作)时包括$0,而$@(或${@})和$*(或${*})不包括。
"变量引用时判断:"B 注,加星号的行表示其他情况正常引用变量值
${var-DEFAULT} 未声明则取默认值*
${var:-DEFAULT} 未声明或值空则取默认值*
${var=DEFAULT} 未声明则赋默认值*
${var:=DEFAULT} 未声明或值空则赋默认值*
${var+OTHER} 已声明则取他值否则取空值
${var:+OTHER} 已声明且值非空则取他值否则取空值
${var?ERR_MSG} 未声明则打印出错信息*
${var:?ERR_MSG} 未声明或值空则打印出错信息*
${!varname} 间接引用,以值为访问变量名称的变量引用其值
${!prefix*} 或 ${!prefix@} 扩展为以前缀开头的变量名,以IFS变量的第一个字符分隔
${!name[*]} 或 ${!name[@]} 扩展为数组中已声明元素的索引,以IFS变量的第一个字符分隔
以上标识符(var,DEFAULT,OTHER,ERR_MSG,varname,prefix,name)可以是任意的,其中,大写的(DEFAULT,OTHER,ERR_MSG)可以是字符串。
"字符串:"B
字符串可以用单引号、双引号或不用引号括起来。
单引号字符串,里面任何字符都照原样且不能出现单引号(转义也不可)。
双引号字符串,里面可以引用变量,可以使用转义。
无引号字符串,变量的值、参数等等都可以看为字符串。
字符串操作:
串长 ${#string}
串址子串 ${string:position} 从position开始的子串
串取子串 ${string:position:length} 从position开始length长度的子串
删除首部最短匹配 ${string#substring} 从首部匹配子串(有多个匹配则取最短的)并删除
删除首部最长匹配 ${string##substring} 从首部匹配子串(有多个匹配则取最长的)并删除
删除尾部最短匹配 ${string%substring} 从尾部匹配子串(有多个匹配则取最短的)并删除
删除尾部最长匹配 ${string%%substring} 从尾部匹配子串(有多个匹配则取最长的)并删除
替换第一个子串 ${string/substring/replacement} 查找第一个匹配的子串并替换之
替换所有子串 ${string//substring/replacement} 查找所有匹配的子串并依次替换之
前缀替换 ${string/#substring/replacement} 若前缀匹配则替换之
後缀替换 ${string/%substring/replacement} 若後缀匹配则替换之
注:string是变量名,position是子串位置(为0则串首,可为负数但要小括号括起来,为-1则串尾),substring可为正则表达式。
以下在bash4.0+被支持:
首字符转大写 ${string^}
所有字符转大写 ${string^^}
首字符转小写 ${string,}
所有字符转小写 ${string,,}
解注:
${parameter^pattern} ${parameter^^pattern}
${parameter,pattern} ${parameter,,pattern}
扩展parameter值并转换其英文字母的大小写(parameter变量不会改变)。
pattern可省略,等同于'?'(匹配每一个字符)。注意,pattern必须是一个字符而非字符串。
'^'操作符将匹配pattern的小写字母转换为大写;','操作符将匹配pattern的大写字母转换为小写。
"^^"和",,"则在扩展后的值中转换每一个匹配的字符,'^'和','操作符只匹配首字符并处理。
若parameter是'@'或'*',则此操作依次作用于每个位置参数并得到结果列表。
若parameter是数组变量且下标是'@'或'*',则此操作依次作用于数组所有成员并得到结果列表。
"数组:"B
数组定义:array_name=(value1 ... valuen) 括号里面允许换行,value以空格或换行分隔
也可以单独定义数组各个分量:array_name[i]=valuei (i可以是数字)
即数组可以不定义就使用,也可以定义时赋初值,数组元素就像普通变量一样使用即可。
引用数组所有元素:${array_name[*]} 或 ${array_name[@]} 只会取得数组已定义元素值的列表
可以使用unset删除数组元素;使用${#array_name[*]}或${#array_name[@]}取得数组已定义的元素数量(数组长度),使用${#array_name[n]}取得n号元素的长度,这些就像求串长一样。
数组切片:${array_name[*]:position:length}或${array_name[@]:position:length},这其实是对数组已定义元素值的列表进行切片,就像串取子串一样。
就是说之前提到的字符串操作实际上是视字符串为列表的操作,同样能作用于数组构建的列表。
"二、shell语句"BS15
"注释语句:"B以'#'开头的行为注释,没有类似C/C++语言的多行注释。
"条件语句:"B
有三种格式:
if [ expression ] then ... fi
if [ expression ] then ... else ... fi
if [ expression ] then ... elif [ expression2 ] then ... else ... fi
可以写成一行也可以多行,如:
if test $[num1] -eq $[num2]; then echo 'equal'; elif test $[num1] -lt $[num2]; then echo 'less'; else echo 'greater'; fi;
或者:
if test $[num1] -eq $[num2]
then
echo 'equal'
elif test $[num1] -lt $[num2]
then
echo 'less'
else
echo 'greater'
fi
"循环语句:"B
for循环:
for var in list
do
command...
done
for循环还支持类C语言格式:
for (( expr1 ; expr2 ; expr3 )) ; do commands ; done
while循环:
while command
do
Statements to be executed if command is true(return 0).
done
until循环:(和while循环测试条件正好相反)
until command
do
Statements to be executed until command is true(return 0).
done
"分支语句:"B
类似C语言的switch-case语句,shell里面有case-esac格式的分支语句:
case var in
mode1)
command...
;;
mode2)
command...
;;
*)
command...
;;
esac
以上分支语句,依次匹配var于mode1,mode2,*,mode是正则表达式,支持'*','?','[abc]','[a-n]'字符和'|'或运算,";;"必须有且在里面表示break(跳出case语句)。
"选择项语句:"B
select name in list
do
statements that can use $name...
done
产生list列表中的菜单项供选择(以数字键选择),然後循环执行do-done之间的语句,在循环中可以用break跳出。另外,select命令使用PS3提示符,默认为"#? ",可以用PS3='string'来改变提示字符串。select语句可以搭配case语句。
"函数:"BCred
shell函数很重要,由于shell逐行执行,shell 函数必须先定义后使用。
[function] funname [()] # function关键字和"()"必须有一个
{
command;
[return int;]
} # 花括号可以换成小括号,区别见特殊字符
以上方括号"[]"中代表可选内容,如果不用return关键字则以最後一条命令的运行结果作为返回值(0-255之间)。"shell函数可以传参,就像命令一样使用函数,就像脚本一样编写函数。"Cred
函数里面可以定义全局变量(默认)和局部变量(local修饰,如:local var=value)。
"local变量的作用域"B
local变量一般只能在函数中定义,它在函数体中有效且会屏蔽同名全局变量,即使是函数中调用的子函数也能访问到该变量,所以一般在函数中定义变量都应该加上local关键字。
"通用退出状态码:"B
0 命令成功结束
1 通用未知错误
2 误用Shell命令
126 命令不可执行
127 没找到命令
128 无效退出参数
128+x Linux信号x的严重错误
130 命令通过Ctrl+C终止
255 退出状态码越界
"三、shell运算"BS15
shell是命令式语言(实际上bash內建了一些命令或扩展来支持运算)。
"算术扩展:"B
((expression)) # 可以用$((expression))引用表达式的值,也可以用$[expression]
表达式值零则返回1否则返回0(退出状态码,0表示true,1表示false)。
expression计算整数运算,写法和C语言的表达式一样(支持同样的运算符),此外还支持:
"**" 指数运算
'#' 进制数,如:"base#n"表示以base为进制的数n,base范围[2,64],数字以此取[0,9],[a,b],[A,B],'@','_'.
"expr命令:"B
expr命令可以实现数值计算、字符串操作,这里只讲有关数值计算(整数运算)的内容:
需注意,不能直接使用关键字和特殊字符(需用'\'转义),用参数传递,然後expr命令输出表达式值,所以参与运算的参数要用空格分开,运算符也要分开。
支持的运算符:数值'+','-','*','/','%',"()";数值或字符串比较'<',"<=",'=',"==","!=",">=",'>'.
注意,以上运算符中,'*'需转义,写作"\*",同样需要转义的字符有:'(',')','<','>'.
"[命令和test命令:"B
[命令是bash内建命令,和test命令作用是等同的,'['调用test命令标识,']'关闭条件判断,因为是命令,所以参数要隔开。
'=' 字符串相等
"!=" 字符串不等
"==" 同'='
'\>' 字符串ASCII码大于
'\<' 字符串ASCII码小于
-z 串长是否值零
-n 串长是否非零
[ str ] 字符串是否非空(注意字符串比较时最好加双引号,以免空字符串导致错误而可能被解析为[ str ]类型)
-eq 数值相等
-ne 数值不等
-gt 数值大于
-lt 数值小于
-ge 数值大于等于
-le 数值小于等于
'!' 逻辑非
-o 逻辑或
-a 逻辑与
-b 文件是否块设备
-c 文件是否字符设备
-d 文件是否目录
-f 文件是否普通文件
-g 文件是否设置了SGID位
-k 文件是否设置了粘着位
-p 文件是否有名管道
-u 文件是否设置了SUID位
-r 文件是否可读
-w 文件是否可写
-x 文件是否可执行
-s 文件是否非空(文件大小非零)
-e 文件是否存在
"[[关键字:"B
[[是bash的关键字而非命令(它不是posix通用的),在[[和]]之间不进行文件名展开和分词,但会参数扩展和命令替换。
文件名展开(filename expansion),例如,*展开为当前工作目录下所有文件。
分词(word splitting),例如,一个带空格的参数可能被分割成多个参数。
[[作用与[作用基本一样,不同的是:
[ ] 使用 [[ ]] 使用
-a -o && ||
\< \> < > 注意别滥用比较运算符(不可用于数值比较)
数学运算符只能在[[ ]]中使用,有:'+','-','*','/','%',另外在[[ ]]中还有:
当使用 `==' 和 `!=' 操作符时,操作符右边的字符串被用作模式并且执行一个匹配。
当使用 `=~' 操作符时,操作符右边的字符串被当作正则表达式来进行匹配。
由于没有文件名展开和分词,所以在字符串比较时可以不加双引号引用变量。
"let命令:"B
let expression # 基本等价于((expression)),但有少许区别:
let用空格分隔多个表达式,而(( ))用逗号分隔。
"declare -i:"B
通过"declare -i",可以定义整型变量,这样就能进行'+','-','*','/','%'运算了(要求参与运算的都是整型变量并且赋值给整型变量),如:
declare -i m n ret
m=10
n=30
ret=$m*$n+m-n # 可以直接用运算符,直接用变量
echo $ret
"bc命令:"B
bc是Linux下的计算器,一般用如下格式:
ret=`echo expression | bc`
如:ret=`echo sqrt\($a\) | bc` 或 ret=`echo "scale=10;sqrt($a)" | bc`
bc计算器可以进行很多种运算,支持浮点运算。
"四、shell特殊字符"BS15
"转义字符:"B
'\a' 响铃(BEL)
'\b' 退格(BS),将当前位置移到前一列
'\c' 配合"echo -e"使用才会转义生效,取消其及之後的所有字符(包括换行符),一般用于echo取消换行符
'\f' 换页(FF),将当前位置移到下页开头
'\n' 换行(LF),将当前位置移到下一行开头
'\r' 回车(CR),将当前位置移到本行开头
'\t' 水平制表符(HT)
'\v' 垂直制表符(VT)
'\\' 反斜杠字符
注意,以下显示有误(当然整篇排版有误影响不大)故用代码如下:
'\ ','\(','\)','\[','\]','\{','\}'等等 用于使之不做特殊用途
"输入输出重定向:"B
文件描述符,除stdin(0),stdout(1),stderr(2)外,用户可定义3~max的文件描述符,可以通过文件描述符重定向输入输出。
输出重定向符:'>'
'>' 覆盖输出
">>" 追加输出
command > filename 把标准输出重定向到一个文件中(覆盖)
command > filename 2>&1 把标准输出和错误一起重定向到一个文件中(覆盖)
command 2 > filename 把标准错误重定向到一个文件中(覆盖)
command 2 >> filename 把标准输出重定向到一个文件中(追加)
command >> filename 2>&1 把标准输出和错误一起重定向到一个文件(追加)
输入重定向符:'<'
command [fd] < file 或文件描述符或设备(fd是文件描述符,此处默认为0)
还有:
'<<<' 字符串输入,如:cat <<< "string"
'<<' 文档输入,如:
cat << EOF > /tmp/file
hello
$name
EOF
给第一个EOF加上双引号,文档内容就不会有变量替换了。
"高级重定向:"BU
exec命令可以更改标准输入输出文件描述符并影响接下来执行的命令。
exec > file # 更改标准输出到文件
exec < file # 更改标准输入自文件
它还可以打开一个文件:exec N<> FILENAME # 重定向符也可使用'<','>'
关闭文件:exec N<&- 或 exec N>&- # exec可省略
文件描述符复制:
[exec] [M]>&N #省略[M]则默认为1 exec是可选项,可换成其他命令
[exec] [M]<&N #省略[M]则默认为0
文件描述符移动:
[exec] [M]>&N- #省略[M]则默认为1 等价于:[exec] [M]>&N;N>&-
[exec] [M]<&N- #省略[M]则默认为0 等价于:[exec] [M]<&N;N<&-
"变量替换:"B
$varname 引用变量
${ } 变量替换,详见shell变量
$( ) 命令替换,等同于` `
$(( )) 替换为表达式值
$[ ] 等同于$(( ))
"其他特殊字符:"B
. 点号,文件包含符,作用同source命令,在目录操作中,也表示当前目录,两个点号则表示上一级目录
# 井号,单行注释,首行#!表示shell类型,或者是在变量替换中有特殊作用
~ 波浪号,帐户的home目录,等价于$HOME
; 分号,分隔连续命令
;; 连续分号,从case语句中跳出
' 单引号,表示字符串,里面任何字符都照原样且不能出现单引号
" 双引号,表示字符串,里面可以引用变量,可以使用转义
` 反引号,命令替换,用于获取其中的命令输出,`command`等价于$(command)
\ 反斜杠,转义字符
| 竖线,管道符,左边命令标准输出作为右边命令标准输入
: 冒号,bash内建命令,仅返回状态值0
? 问号,文件名展开中代表任意一个字符
* 星号,文件名展开中代表任何字符串
$ 钱号,变量替换
( ) 小括号,命令群组(会生成子shell),在'$( )'中做命令替换,用于定义函数,还用于定义数组
(( )) 双小括号,bash的算术扩展,在变量替换中引用表达式值,还用于C语言风格的for循环
{ } 大括号,命令群组(不生成子shell),相当于匿名函数;在文件名展开中做通配符扩展
[ ] 方括号,等同test命令,或在'$[ ]'中等价于'$(( ))',或者是在通配符中匹配范围内一个字符
[[ ]] 双方括号,[[关键字,用于某些运算
|| shell逻辑或,从左向右执行,直到命令返回成功或执行完毕
&& shell逻辑与,从左向右执行,直到命令返回失败或执行完毕
! shell逻辑非,将命令返回值取非,若command返回0则! command返回1,否则! command返回0
& 与号,置于命令结尾,表示将该命令置于後台工作,或者是在文件描述符中有特殊作用
^ 在通配符中表示匹配范围之外,如:[^0-9]将不匹配数字,注意,通配符不被bash展开但被某些命令接受
> >> 输出重定向
< << <<< 输入重定向
- 减号,在某些命令中表示标准输入或输出流
<(command) 进程替换,command命令置于後台异步写入到一个虚拟文件,<(command)被替换为该虚拟文件名
>(command) 进程替换,command命令置于後台异步读取到一个虚拟文件,<(command)被替换为该虚拟文件名
"文件名展开中的通配符扩展"U
大括号的作用,注意展开後得到的是结果列表,如:
{a,b,c}{1,2} 展开为 a1 a2 b1 b2 c1 c2
{1..5} 展开为 1 2 3 4 5
"五、shell命令"BS15
echo 屏幕输出
printf 格式输出(类似C语言的printf函数,format_string的含义都一样)
printf format_string [arguments...] 如:printf %d $var1 123
与C语言的printf函数区别如下:
printf 命令不用加括号
format_string 可以没有引号,但最好加上,单引号双引号均可。
参数多于格式控制符(%)时,format_string 可以重用,可以将所有参数都转换。
arguments 使用空格分隔,不用逗号。
source 文件包含,如:source filename.sh
shift 删除参数列表中前n个参数,默认删除第一个参数