Linux Shell精简教程
Linux Shell教程
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,详细的请自查:
一、shell变量
- 定义变量,使用赋值语句,如:
variableName=value(等号两边没有空格)。 - 引用变量,用
'$'引用变量,如:$variableName或${variableName}(花括号限定边界)。 - 重定义变量,直接定义即可。
- 取消变量定义,用unset,如:
unset variableName(不能删除只读变量) - 只读变量,可以将已定义变量改为只读,如:
readonly variableName - 显示所有本地shell变量,用set命令即可。
变量类型:
- 局部变量
- 环境变量
- shell特殊变量(命令行参数是特殊变量)
命令行参数
- 引用命令行参数
$0当前脚本的文件名$#传递给脚本或函数的参数个数(不包括$0)。$*传递给脚本或函数的所有参数(不包括$0)。$@传递给脚本或函数的所有参数(不包括$0)。被双引号包含时,与$*稍有不同,下面将会单独讲到。$?上个命令的退出状态,或函数的返回值。成功返回0,否则非零(一般为1)。$$当前进程的ID$n传递给脚本或函数的第n个参数。例如,第一个参数是$1,第二个参数是$2
如果获取第十个及其後的命令行参数,用${n}的形式。
$*和$@的区别:
前者将参数当做一个整体,後者将参数作为个体连接。所以,$*和$@是一样的,而"$*"和"$@"不一样。
IFS分隔符:
"$\*" 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'(换行符)。
${*}和${@}的变量替换:
此两者的区别在变量替换,在${ }中使用*和@是有区别的:
使用@得到结果列表,使用*得到一个字符串,如:
${@:0}得到$0$1$2... 的结果列表${*:0}得到$0$1$2... 的单个字符串
注意,@和*在花括号里面进行某些高级替换(见字符串操作)时包括$0,而$@(或${@})和$*(或${*})不包括。
变量引用时判断
注:加星号的行表示其他情况正常引用变量值。
| 引用方式 | 说明 |
|---|---|
${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)可以是字符串。
字符串
- 字符串可以用单引号、双引号或不用引号括起来。
- 单引号字符串,里面任何字符都照原样且不能出现单引号(转义也不可)。
- 双引号字符串,里面可以引用变量,可以使用转义。
- 无引号字符串,变量的值、参数等等都可以看为字符串。
- 字符串操作:
| 操作 | 方式 | 说明 |
|---|---|---|
| 串长 | ${#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是数组变量且下标是'@'或'*',则此操作依次作用于数组所有成员并得到结果列表。
数组
- 数组定义:
array_name=(value1 ... valuen)
括号里面允许换行,value以空格或换行分隔 - 也可以单独定义数组各个分量:
array_name[i]=valuei(i可以是数字).
即数组可以不定义就使用,也可以定义时赋初值,数组元素就像普通变量一样使用即可。 - 引用数组所有元素:
${array_name[*]}或${array_name[@]}.
只会取得数组已定义元素值的列表 - 可以使用unset删除数组元素:
unset array_name[i] - 使用
${#array_name[*]}或${#array_name[@]}取得数组已定义的元素数量(数组长度) - 使用
${#array_name[n]}取得n号元素的长度,这些就像求串长一样。 - 数组切片:
${array_name[*]:position:length}或${array_name[@]:position:length}.
这其实是对数组已定义元素值的列表进行切片,就像串取子串一样。
注:就是说之前提到的字符串操作实际上是视字符串为列表的操作,同样能作用于数组构建的列表。
二、shell语句
注释语句
以'#'开头的行是注释,没有类似C/C++语言的多行注释。
条件语句
有三种格式:
if [ expression ] then ... fiif [ expression ] then ... else ... fiif [ 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;
或者:
1 | |
循环语句
for循环:
for var in list do command... donefor循环还支持类C语言格式:
for (( expr1 ; expr2 ; expr3 )) ; do commands ; donewhile循环:
while command do Statements to be executed if command is true(return 0). doneuntil循环:(和while循环测试条件正好相反)
until command do Statements to be executed until command is true(return 0). done
分支语句
类似C语言的switch-case语句,shell里面有case-esac格式的分支语句:
case var in
mode1)
command...
;;
mode2)
command...
;;
*)
command...
;;
esac
以上分支语句,依次匹配var于mode1,mode2,*,mode是正则表达式,支持'*','?','[abc]','[a-n]'字符和'|'或运算,";;"必须有且在里面表示break(跳出case语句)。
选择项语句
select name in list
do
statements that can use $name...
done
产生list列表中的菜单项供选择(以数字键选择),然後循环执行do-done之间的语句,在循环中可以用break跳出。另外,select命令使用PS3提示符,默认为"#? ",可以用PS3='string'来改变提示字符串。select语句可以搭配case语句。
函数
shell函数很重要,由于shell逐行执行,shell 函数必须先定义后使用。
[function] funname [()] # function关键字和"()"必须有一个
{
command;
[return int;]
} # 花括号可以换成小括号,区别见特殊字符
- 以上方括号
"[]"中代表可选内容。 - 如果不用return关键字则以最後一条命令的运行结果作为返回值(0-255之间)。
- shell函数可以传参,就像命令一样使用函数,就像脚本一样编写函数。
- 函数里面可以定义全局变量(默认)和局部变量(local修饰,如:
localvar=value)。- local变量的作用域
local变量一般只能在函数中定义,它在函数体中有效且会屏蔽同名全局变量,即使是函数中调用的子函数也能访问到该变量,所以一般在函数中定义变量都应该加上local关键字。
- local变量的作用域
通用退出状态码
| exit code | 说明 |
|---|---|
| 0 | 命令成功结束 |
| 1 | 通用未知错误 |
| 2 | 误用Shell命令 |
| 126 | 命令不可执行 |
| 127 | 没找到命令 |
| 128 | 无效退出参数 |
| 128+x | Linux信号x的严重错误 |
| 130 | 命令通过Ctrl+C终止 |
| 255 | 退出状态码越界 |
三、shell运算
shell是命令式语言(实际上bash內建了一些命令或扩展来支持运算)。
算术扩展
((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命令
expr命令可以实现数值计算、字符串操作,这里只讲有关数值计算(整数运算)的内容:
需注意,不能直接使用关键字和特殊字符(需用''转义),用参数传递表达式,然後expr命令输出表达式值,所以参与运算的参数要用空格分开,运算符也要分开。
支持的运算符:
- 数值
'+','-','*','/','%',"()". - 数值或字符串比较
'<',"<=",'=',"==","!=",">=",'>'.
注意,以上运算符中,'*'需转义,写作"\*",同样需要转义的字符有:'(',')','<','>'.
[命令和test命令
[命令是bash内建命令,和test命令作用是等同的,'['调用test命令标识,']'关闭条件判断,因为是命令,所以参数要隔开。
| 选项 | 说明 |
|---|---|
'=' |
字符串相等 |
"!=" |
字符串不等 |
"==" |
同'=' |
'>' |
字符串ASCII码大于 |
'<' |
字符串ASCII码小于 |
-z |
串长是否值零 |
-n |
串长是否非零 |
[ str ] |
字符串是否非空 |
-eq |
数值相等 |
-ne |
数值不等 |
-gt |
数值大于 |
-lt |
数值小于 |
-ge |
数值大于等于 |
-le |
数值小于等于 |
'!' |
逻辑非 |
-o |
逻辑或 |
-a |
逻辑与 |
-b |
文件是否块设备 |
-c |
文件是否字符设备 |
-d |
文件是否目录 |
-f |
文件是否普通文件 |
-g |
文件是否设置了SGID位 |
-k |
文件是否设置了粘着位 |
-p |
文件是否有名管道 |
-u |
文件是否设置了SUID位 |
-r |
文件是否可读 |
-w |
文件是否可写 |
-x |
文件是否可执行 |
-s |
文件是否非空(文件大小非零) |
-e |
文件是否存在 |
注意:字符串比较时最好加双引号,以免空字符串导致错误而可能被解析为[ str ]类型。
[[关键字
[[是bash的关键字而非命令(它不是posix通用的),在[[和]]之间不进行文件名展开和分词,但会参数扩展和命令替换。
- 文件名展开(filename
expansion),例如,
*展开为当前工作目录下所有文件。 - 分词(word splitting),例如,一个带空格的参数可能被分割成多个参数。
[[作用与[作用基本一样,不同的是:
[ ]使用 |
- | [[ ]]使用 |
- |
|---|---|---|---|
-a |
-o |
&& |
|| |
< |
> |
< |
> |
注意:别滥用比较运算符(不可用于数值比较)。
数学运算符只能在[[ ]]中使用,有:'+','-','*','/','%',另外在[[ ]]中还有:
- 当使用
==和!=操作符时,操作符右边的字符串被用作模式并且执行一个匹配。 - 当使用
=~操作符时,操作符右边的字符串被当作正则表达式来进行匹配。 - 由于没有文件名展开和分词,所以在字符串比较时可以不加双引号引用变量。
let命令
let expression
基本等价于((expression)),但有少许区别:
let用空格分隔多个表达式,而(( ))用逗号分隔。
declare -i
通过"declare -i",可以定义整型变量,这样就能进行'+','-','*','/','%'运算了(要求参与运算的都是整型变量并且赋值给整型变量),如:
declare -i m n ret
m=10
n=30
ret=$m*$n+m-n # 可以直接用运算符,直接用变量
echo $ret
bc命令
bc是Linux下的计算器,一般用如下格式:
ret=`echo expression | bc`
如:ret=`echo sqrt\($a\) | bc` 或 ret=`echo "scale=10;sqrt($a)" | bc`
bc计算器可以进行很多种运算,支持浮点运算。
四、shell特殊字符
转义字符
| 转义字符 | 说明 |
|---|---|
'\a' |
响铃(BEL) |
'\b' |
退格(BS),将当前位置移到前一列 |
'\c' |
取消其及之後的所有字符(包括换行符) |
'\f' |
换页(FF),将当前位置移到下页开头 |
'\n' |
换行(LF),将当前位置移到下一行开头 |
'\r' |
回车(CR),将当前位置移到本行开头 |
'\t' |
水平制表符(HT) |
'\v' |
垂直制表符(VT) |
'\\' |
反斜杠字符 |
'\ ','\(','\)','\[','\]','\{','\}'等等,用于使之不做特殊用途。
注意:'\c'要配合"echo -e"使用才会转义生效,,一般用于echo取消换行符。
输入输出重定向
文件描述符,除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加上双引号,文档内容就不会有变量替换了。
- 管道符:
'|'
'command1 | command2'左边命令标准输出作为右边命令标准输入'command1 |& command2'左边命令标准输出和标准错误作为右边命令标准输入
注意:管道符两边的命令会作为子Shell执行,而子Shell无法更改父环境的变量。
高级重定向
exec命令可以更改标准输入输出文件描述符并影响接下来执行的命令。
exec > file # 更改标准输出到文件 exec < file # 更改标准输入自文件exec命令还可以打开一个文件:
exec N<> FILENAME # 重定向符也可使用'<','>'分读写打开关闭文件:
exec N<&- 或 exec N>&- # exec可省略文件描述符复制:(文件描述符N复制到M)
[exec] [M]>&N #省略[M]则默认为1 [exec] [M]<&N #省略[M]则默认为0[]中的是可选项,exec可换成其他命令。文件描述符移动:(文件描述符N移动到M)
[exec] [M]>&N- #省略[M]则默认为1,等价于:[exec] [M]>&N;N>&- [exec] [M]<&N- #省略[M]则默认为0,等价于:[exec] [M]<&N;N<&-
变量替换
| 引用方式 | 说明 |
|---|---|
$varname |
引用变量 |
${ } |
变量替换,详见shell变量 |
$( ) |
命令替换,等同于` ` |
$(( )) |
替换为表达式值 |
$[ ] |
等同于$(( )) |
其他特殊字符
| 符号 | 说明 |
|---|---|
. |
点号,文件包含符,作用同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)被替换为该虚拟文件名 |
文件名展开中的通配符扩展
大括号的作用,注意展开後得到的是结果列表,如:
{a,b,c}{1,2} 展开为 a1 a2 b1 b2 c1 c2
{1..5} 展开为 1 2 3 4 5
五、shell命令
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.shshift 删除参数列表中前n个参数,默认删除第一个参数
bash 打开一个bash环境,可用于执行脚本,例如:
curl -L $url | bash -s args获取网络上的脚本以args为参数执行,又或者:bash -c "$(curl -L $url)" @ args亦同,获取脚本并执行,@作为脚本参数$0