【Linux】shell语法入门手册 语法大全
shell学习笔记 yxc的linux
shell语法目录
- 概论
-
-
- 运行方式
-
-
- 直接用解释器执行
- 作为可执行文件运行
-
-
- 注释
-
-
- 单行注释
- 多行注释
-
- 变量
-
-
- 定义变量
- 引用变量
- 只读变量
- 删除变量
- 变量类型
- 字符串
-
- 默认变量
-
-
- 文件参数变量
- 其他参数相关变量
-
- 数组
-
-
- 定义
- 调用数组元素中的值
- 数组长度
-
- expr命令
-
- 重要说明
-
- 字符串表达式
- 整数表达式
- 逻辑关系表达式
- read命令
- echo命令
-
-
- 显示普通字符串
- 显示转义字符
- 显示变量
- 显示换行
- 显示不换行
- 显示结果定向至文件
- 原样输出
- 显示命令执行结果
-
- printf命令
- test命令与判断符号[]
-
-
- 逻辑运算符
- test
-
-
- 文件类型判断
- 文件权限判断
- 整数间的比较
- 字符串比较
- 多重条件判定
-
- 判断符号[]
-
- 判断语句
-
-
- 单层if
- 单层if else
- if-elif-elif-else
- case
-
- 循环语句
-
-
- for-in-do-done
- for((...;...;...))do...done
- while...do...done循环
- until...do...done循环
- break命令
- continue命令
- 死循环处理
-
- 函数
-
-
- 调用函数
- 函数的输入参数
- 函数内的局部变量
-
- exit命令
- 文件重定向
-
-
- 重定向命令
-
- 引入外部脚本
- 附录
概论
shell是我们通过命令行与操作系统沟通的语言。它是一个解释性语言,不需要编译运行。
shell脚本可以直接在命令行中执行,也可以将一套逻辑组织成一个文件,方便复用。
而我们使用的终端,可以将其想象成一个大的文件,在逐行执行命令。
Linux常见shell脚本有很多种,一般默认使用bash。文件开头指明脚本解释器 #! /bin/bash
运行方式
新建一个文件,bash文件后缀是.sh
#! /bin/bashecho "Hello World!"
直接用解释器执行
root@kali:~$ bash filename.shHello World! # 脚本输出
作为可执行文件运行
首先要赋予文件权限
root@kali:~$ chmod +x filename.sh #使脚本具有可执行权限x
接下来有三种执行方式,一般第一种最常用
root@kali:~$ ./filename.sh #当前路径下执行Hello World! # 脚本输出
root@kali:~$ /home/root/filename.sh #绝对路径下执行Hello World! # 脚本输出
root@kali:~$ ~/filename.sh # 家目录下执行Hello World! # 脚本输出
关于文件权限的问题——跳转至附录
注释
单行注释
#
后均为注释
#注释echo 'Hello World' #注释
多行注释
:<<EOF注释1注释2注释3EOF
其中EOF可以替换为任意字符串
变量
定义变量
定义字符串,以下三种都可以
name='Genevieve_xiao'name="Genevieve_xiao"name=Genevieve_xiao
引用变量
加上$
或${}
表示引用变量,{}
可省略,但是遇到变量边界歧义时最好加上。
name=Genevieve_xiaoecho $nameecho ${name}hahaha
只读变量
readonly
或者declare
再加变量
name=Genevieve_xiaoreaonly namedeclare -r namename=hahaha #报错
删除变量
unset
name=Genevieve_xiaounset nameecho $name #输出空行
变量类型
分为自定义变量(局部)和环境变量(全局)。
子进程不能访问自定义变量。
name=Genevieve_xiao #自定义变量export name #method1declare -x name #method2局部变全局
export name=Genevieve_xiao #定义环境变量declare +x name #全局变局部
字符串
单双引号的区别:
- 单引号内容会原样输出,不会执行、不取变量、不会转义
- 双引号内容可以执行、可以取变量、可以转义
不加引号同双引号效果。
获取字符长度
name=Genevieve_xiaoecho ${#name}
提取子串
nam=Genevieve_xiaoecho ${name:0:5}
默认变量
文件参数变量
在执行shell时向脚本传递的参数。
$0
是./文件名
$1
第一个参数
$2
第二个参数
#! /bin/bashecho $0echo $!echo $2
root@kali:~$ chmod +x filename.shroot@kali:~$ ./filename.sh 1 2./filename.sh #脚本输出$01 #脚本输出$12 #脚本输出$2
其他参数相关变量
$#
文件传入的参数个数$*
所有参数构成的由空格隔开的字符串"$1 $2"
$@
每个参数分别由双引号括起来的字符串"$1" "$2"
$$
脚本当前运行的进程ID$?
上一条命令的退出状态exit code。0表示正常退出,其他值表示错误$(commmand)
返回这条指令的stdout 可嵌套'command'
返回这条指令的stdout 不可嵌套
数组
可以存放不同类型的值,只支持一维数组,初始化不用指明数组大小,下标从零开始。
定义
array=(1 abs "hahaha" bala)
或者
array[0]=1array[1]=absarray[2]="hahaha"array[3]=bala
调用数组元素中的值
调用单个元素
${array[index]}
调用整个数组
${array[@]}${array[*]}
数组长度
${#array[@]}${#array[*]}
expr命令
用于求表达式的值 expr 表达式
重要说明
- 用空格隔开每一项
- 用反斜杠放在shell特定的字符前面(转义)
- 对包含空格和其他特殊字符的字符串要用单引号括起来
- expr会在stdout中输出结果,不需要嵌套echo。如果为逻辑关系表达式,若结果为真,stdout为1,否则为0。
- expr的exit code:如果为逻辑关系表达式,若结果为真,exit code为0,否则为1。
- 在这里出现的string 下标从1开始
- 优先级:字符串表达式>算术表达式>逻辑关系表达式
字符串表达式
lengrh STRING
返回string的长度index STRING CHARSET
任意cherset中的单个字符在string最先出现的位置 下标从1开始 如果不存在返回0substr STRING POSITION LENGTH
返回string从position开始长度为length的子串 下标从1开始。若pos或len不合法则返回空字符串
str="Hello World!"expr length "$str" #输出12expr index "$str" abcde #输出2expr substr "$str" 2 3 #输出ell
整数表达式
+ -
* / %
()
注意*
和()
需要转义
expr $a + $bexpr $a - $bexpr $a \* $bexpr $a / $bexpr $a % $bexpr \( $a + 1 \) \* \( $b + 1 \)
逻辑关系表达式
|
如果第一个参数非空且非0,则返回第一个参数的值且忽略第二个参数,否则返回第二个参数的值,但要求第二个参数的值也是非空或非0,否则返回0。&
如果两个参数都非空且非0,则返回第一个参数,否则返回0。若第一个参数为0,则直接忽略第二个参数并返回0。< = >
如果为true则返回1,否则返回0。这里==与=等价()
a=3b=0expr $a \> $b #1expr $a '>' $b #1expr $a \& $b #0expr $a \| $b #3
read命令
标准输入中读取单行数据。当读到文件结束符时,exit code
为1,即返回假,否则为0 ,循环语句中会用到
参数
-p
接提示信息-t
跟秒数,超过等待时间则自动忽略此条命令
read -p "input u name:" -t 30 name
echo命令
用于输出字符串echo SRING
显示普通字符串
echo "Hello World"echo Hello World
显示转义字符
echo "\"Hello World\""echo \"Hello World\"
显示变量
name=Genevieve_xiaoecho "My name is $name"
显示换行
-e
开启转义
echo -e "hi\nhahaha"
输出
hihahaha
显示不换行
\c
不换行
echo -e "hi \c"echo "hahaha"
输出
hi hahaha
显示结果定向至文件
echo "Hello World" > output.txt
相当于直接创建了一个新的文件output.txt并将输出结果放进去
原样输出
单引号
echo '\"$name"'
输出
\"name"
显示命令执行结果
注意是反引号`
而不是单引号 '
.
echo `expr 3 + 4`
输出
7
printf命令
用于格式化输出,类似于c中的printf函数
默认不添加换行符
printf format-string [arguments...]
举例
printf "%d * %d =%d\n" 5 6 `expr 5 \* 6`
就相当于是printf函数去掉了括号和逗号分隔符,shell里的分隔符是空格
test命令与判断符号[]
逻辑运算符
&&
与,||
或- 用于连接两个expr,而单个的
& |
则是在expr里面进行逻辑关系运算 - 短路原则
- exit code 为0,表示真,非0,表示假
test
test命令用exit code返回结果,不同于expr用stdout返回
test用于判断文件类型,以及对变量做比较。
一个很妙的运算
test -e filename.sh && echo "exist" || echo "not exist"
如果文件存在,则第一项为真,执行&&
后的那一项,也就是第二项,输出exist,这样||
前面为真,则直接忽略第三项;
如果文件不存在,则第一项为假,自动忽略第二项,执行第三项, 输出not exist。
文件类型判断
-e
是否存在-f
是否为文件-d
是否为目录
文件权限判断
格式test -r fliename
-r
是否可读-w
是否可写-x
是否可执行-s
是否为非空文件
整数间的比较
格式test $a -eq $b
-eq
等于-ne
不等于-gt
大于-lt
小于-ge
大于等于le
小于等于
这几个是用于数值间的比较,而符号是用于字符串间的比较。
字符串比较
-z
是否为空-n
是否非空==
是否等于!=
是否不等于
test -z $strtest -n $strtest str1==str2test str1!=str2
多重条件判定
格式test -r filename -a -x filename
-a
all两个条件是否同时成立-o
or两个条件是是否至少一个成立!
取反 例如!-x
判断符号[]
与test用法几乎一样,更常用与if语句中,[[]]是[]的加强版。
不过本质上[
是个命令,]
是个标志。
[ -e filename.sh ] && echo "exist" || "not exist"
注意
- 每一项用空格隔开
- 变量最好用双引号引起来
- 常数最好用单引号或双引号引起来
如果不引起来的话,万一变量里有空格,就有可能会报错。
一般来说,比起使用expr和test,在进行逻辑关系表达时,更多地还是使用[]
判断语句
所有的if或者elif后面都有then
单层if
格式
if conditionthen语句1语句2..fi
单层if else
if conditionthen语句1语句2...else语句1语句2...fi
if-elif-elif-else
if conditionthen语句1语句2...elif conditionthen语句1语句2...elif conditionthen语句1语句2...else语句1语句2...fi
case
类似于c的switch
case $变量 in值1)语句1语句2...;;值2)语句1语句2...;;*)语句1语句2...;;esac
循环语句
for-in-do-done
for var in val1 val2 val3do语句1语句2...done
举例
依次输出参数
for i in a 9 ssdo echo $idone
依次输出1-10
for i in $(seq 1 10) #seq序列doecho $idone
依次输出a-z
for i in {a..z} #可以数字 可以倒序doecho $idone
依次输出当前目录的文件名
for file in `ls`doecho $filedone
for((…;…;…))do…done
类似于c中的for
for ((expression; condition; expression))do 语句1语句2done
这里的expression不需要转义
for ((i=1 ;i<=10 :i++))doecho $idone
while…do…done循环
while conditiondo语句1语句2...done
文件结束符为ctrl+D
,输入结束符后read指令返回false,循环停止。
until…do…done循环
当条件为真时结束。
until conditiondo 语句1 语句2 ..done
break命令
跳出当前一层循环,但与c不同的是,如果break出现在case里的话,break不是跳出case语句,而是忽略case,跳出case外的一个循环语句。
while read namedo for ((i=1;i<=10;i++)) do case $i in 8) break ;; *) echo $i ;; esac donedone
该实例里每次输入非EOF的字符串,会输出一遍1-7。
在for语句读到8的时候,break掉了for语句,进入下一次while。
continue命令
同c里的continue,跳过当前这次循环。
死循环处理
两种方法
- ctrl+c
- top命令找到进程PID,输入
kill -9 PID
函数
跟c里的函数基本一样,但是return值不同,是exitcode ,只能是0-255之间的数,0表示正常结束。
若要调用函数的输出结果,可通过echo输出到stdout中再通过$(function_name)
来获取
return值通过$?
获取。
调用函数的时候不需要写()。
格式
[function] func_name(){ #function 可不写语句1语句2...}
调用函数
func(){name=Gxecho $name}func
输出
Gx
若要获取return和stdout
func(){name=Gxecho $nanereturn 111}output=${func} #or`func`ret=$?echo "output=$output"echo "return=$ret"
输出
ouput=Gxreturn=111
函数的输入参数
$1
表示第一个输入参数,$2
表述第二个输入参数
$0
仍然是文件名。
func() { # 递归计算 $1 + ($1 - 1) + ($1 - 2) + ... + 0 word="" while [ "${word}" != 'y' ] && [ "${word}" != 'n' ] do read -p "要进入func($1)函数吗?请输入y/n:" word done if [ "$word" == 'n' ] then echo 0 return 0 fi if [ $1 -le 0 ] then echo 0 return 0 fi sum=$(func $(expr $1 - 1)) echo $(expr $sum + $1)}echo $(func 10) #输出55
注:为什么只输出了55而没有输出其他echo的值呢,只要在echo后面有$(),它就会截获stdout里的值。递归往回调用的时候上一层的echo会被这一层的sum=$()截获而没有输出,而最后一层的echo被函数外的$捕获,函数外的echo输出55.
函数内的局部变量
变量未经声明都是全局,除非特殊命名。
格式local 变量名=变量值
exit命令
exit命令用来退出当前shell进程,并返回一个退出状态;使用$?可以接收这个退出状态。
exit命令可以接受一个整数值作为参数,代表退出状态。如果不指定,默认状态值是 0。
exit退出状态只能是一个介于 0~255 之间的整数,其中只有 0 表示成功,其它值都表示失败。
exit n
返回n并退出进程
文件重定向
每个进程默认打开3个文件描述符:
- stdin标准输入,从命令行读取数据,文件描述符为0
- stdout标准输出,向命令行输出数据,文件描述符为1
- stderr标准错误输出,向命令行输出数据,文件描述符为2
可以用文件重定向将这三个文件重定向到其他文件中。
重定向命令
command > file
将stdout重定向到file中command < file
将stdin重定向到file中command >> file
将stdout以追加方式重定向到file中command n> file
将文件描述符n重定向到file中command n>> file
将文件描述符n以追加方式重定向到file中
stdout和stdin可以同时重定向
test.sh<input.txt>output.txttest.sh<output.txt>input.txt #这两个是等价的
引入外部脚本
类似于c的include,引入其他文件中的代码
两种格式
. filename
source filename
filename可以写绝对路径
相当于把这个外部脚本在当前文件中展开
举例
创建test1.sh
#! /bin/bashname=Gx
创建test2.sh
#! /bin/bashsource test1,txtecho My name is :$name
输出
My name is Gx
附录
关于文件权限的问题
如何查看文件权限呢
root@kali:~$ ls -l filename.sh
-rw-rw-r-- ... (省略)... # 脚本输出
脚本输出这里的123位rw-
是作者user权限,456位rw-
是同用户组group权限,789位r--
是其他用户other权限,而第0位若是-
则表示普通文件file,若是d
则表示目录directory。
r指read 可读权限(4),w指write 可写入权限(2),x指execute 可执行权限(1)。
顺便简单介绍一下chomd的用法
字母法公式
chomd [ u g o a ] [ + - = ] [ r w x ] filename
数字法公式chomd [ num1 ][ num2 ][ num3 ] filename
u-user,g-group,o-other,a-all
r-read-4,w-write-2,x-execute-1
num1-user的权限和,num2-group的权限和,num3-other的权限和。
+增加权限,-撤销权限,=赋予权限。
不写ugoa默认就是all。
举例1
chomd u+rwx, g+rw, o+rw filename.sh
等价于chomd 755 filename.sh