shell语法基础

本篇博文总结常用的shell编程语法,有问题可及时在本博客或个人博客下留言。

shell编程基础

shell脚本首行

bash shell脚本首行#!/bin/bash,用于确保bash作为脚本的解释程序,固定格式,记住即可,当不指定首行时,默认采用/bin/sh作为解释程序。

#!/bin/bash

shell脚本注释

shell中添加注释方法如下:

#第一种,"#"用于注释单行
#第二种,:<<! 注释内容 !
#第三种,<<COMMENT 注释内容 COMMENT
#第四种,<<EOF 注释内容 EOF
执行如下脚本
#!/bin/bash
echo "first word"
:<<!
echo "这是第一种注释方法"
!
#echo 这是第二种注释方法
<<COMMENT
echo "这是第三种注释方法"
COMMENT
<<EOF
echo "这是第四种注释方法"
EOF
echo "last word"
输出
first word
last word

shell脚本执行权限

shell脚本要想执行,必须具有执行权限,设置权限的命令可以查看权限设置,下面给出例子:

#给当前用户添加执行权限
chmod u+x xxx.sh
#给所有用户添加执行权限
chmod +x xxx.sh 
chmod 777 xxx.sh

运行shell脚本,可以通过绝对路径或相对路径

/home/scripts/myshell.sh
#或者先进入路径目录,在执行
cd /home/scripts/
./myshell.sh

想要在任何路径都能直接通过脚本名执行脚本,需要将shell脚本所在路径添加到path环境变量

export PATH=$PATH:/home/scripts

之后在任意路径直接myshell.sh即可执行脚本

shell变量扩展

基本的shell变量拓展

在之前已经讲过,采用${param}

${param}
间接参数拓展

${!param}表示引用的参数不是param自身,而是其对应的值,比如param的值是temp,通过${!param}将拓展为参数temp的值,如下:

var=temp
temp="this is realme"
echo $var
temp
echo ${var}
temp
echo ${!var}
this is realme
大小写修改

${param^},将首字符改为大写,${param^^}所有字符改为大写

${param,}将首字符改为小写,${param,,}所有字符改为小写

${param~}将首字符大小写反转(原来大写改为小写,原来小写改为大写);${param~~}将所有字符大小写反转

var="this is real me"
echo ${var^}
This is real me
echo ${var^^}
THIS IS REAL ME
var2="THIS IS REAL ME"
echo ${var2,}
tHIS IS REAL ME
echo ${var2,,}
this is real me
echo ${var2~}
tHIS iS rEAL mE
echo ${var2~~}
this is real me
echo ${var~}
This Is Real Me
echo ${var~~}
THIS IS REAL ME
var3="tHis Is mE"
echo ${var3~}
THis is ME
echo ${var3~~}
ThIS iS Me
变量名拓展

${!prefix*}或${!prefix@},使用变量名拓展将列出以字符串prefix为前缀的所有变量名,默认以空格隔开

var=1
var2=2
var3=3
echo ${!var*}
var var2 var3
echo ${!var@}
var var2 var3
字符串移除

${param#parttern}

${param##parttern}

${param%parttern}

${param%%parttern}

上述前两个语句从开头开始匹配移除,后两个语句从结尾开始匹配移除,其中单个的"#","%"表示移除匹配指定模式的最短文本,两个的表示移除匹配指定模式的最长文本,直接看例子:

filename=myshell.sh
#保留文件名后缀
echo ${filename##*.}
sh
#移除后缀保留文件名
echo ${filename%.*}
myshell
filename2=/usr/logs/mylog.log
#移除文件名保留路径
echo ${filename2%/*}
/usr/logs
#移除路径保留文件名
echo ${filename2##*/}
mylog.log
字符串搜索与替换

${param/pattern/string}

${param//pattern/string}

${param/pattern}

${param//pattern}

操作符"/“表示替换一个匹配的字符串,操作符”//"表示替换所有匹配的字符串,如果没有指定匹配的字符串,那么匹配的内容将会被替换为空字符串也就是删除掉。

var="I think I understand you"
echo ${var/I/U}
U think I understand you
echo ${var//I/U}
U think U understand you
echo ${var//I/}
think understand you
echo ${var/I/}
think I understand you
echo ${var/I}
think I understand you
echo ${var//I}
think understand you
求字符串长度
${#param}
echo ${#var}
24
子字符串扩展

${param:offset}

${param:offset:length}

从指定位置开始截取指定长度的字符串,如果省略length,将截取到参数值末尾。

echo ${var:1}
think I understand you
echo ${var:0}
I think I understand you
echo ${var:0:9}
I think I
使用默认值

${param:-word}

${param-word}

第一种,当param参数为未定义或者为null(shell中指为空字符串而不是null)时,输出word,否则输出param;第二种只用在param未定义,才会输出word。

var="I know U"
echo ${var:haha}
I know U
echo ${var:-haha}
I know U
echo ${var-haha}
I know U
#以下为var为null字符串时,注意此时不是null,和上面的字符串结果一样
var=null
echo ${var}
null
echo ${var:haha}
null
echo ${var:-haha}
null
echo ${var-haha}
null
#以下为var未定义时
unset var
echo ${var}

echo ${var:haha}

echo ${var-haha}
haha
echo ${var:-haha}
haha
#以下为var为null时,直接理解成为空字符串
var=""
echo ${var}

echo ${var-haha}

echo ${var:-haha}
haha
echo ${var:haha}

指定默认值

${param:=word}

${param=word}

这种模式和使用默认值的输出类似,区别在于这种模式会将word赋值给param,作为param的值

echo ${var}

echo ${var:=haha}
haha
echo ${var}
haha
echo ${var=haha}
haha
echo ${var}
haha
var=""
echo ${var:=haha}
haha
echo ${var=haha}
haha
var=nihao
echo ${var=haha}
nihao
echo ${var:=haha}
nihao
使用替代值

${param:+word}

${param+word}

如果param未定义或者为空,不输出任何内容,如果已定义且不为空,输出word,且不会拓展为param的值

echo ${age}

echo ${age:+"nihao"}

age=""
echo ${age}

echo ${age:+"nihao"}

age="18"
echo ${age:+"nihao"}
nihao
echo ${age+"nihao"}
nihao
echo ${age}
18

Bash内部变量

编写shell脚本时注意不要与shell内部变量重合,可以通过env查看现有的系统变量,下面是一些内部变量(使用env不一定会展示下面变量)

$BASH-引用Bash实例的全路径名
$HOME-当前用户的home目录
$IFS-内部字段分隔符
$OSTYPE-操作系统的类型
$SECONDS-脚本已运行的秒数
$UID-当前用户的账号标识码(ID),与/etc/passwd中记录的相同

Bash中的位置参数和特殊参数

位置参数

位置参数是由除0意外的一个或多个数字表示的参数,当shell或shell的函数被引用时,由shell或shell函数的参数赋值,并且可以使用bash内部命令set来重新赋值,位置参数N引用时语法为${N},当位置顺序为个位数时,可以写成$N,超过一位数必须加大括号

位置参数可以用来给shell脚本指定参数,不能通过赋值语句来赋值,只能通过bash内部命令setshift来设置和取消,shell脚本运行是,位置参数会被临时地替换。

#如下脚本test.sh
#!/bin/bash
echo "param1:$1"
echo "param2:$2"
echo "param3:${3}"
#运行脚本并输入参数
./test.sh "参数一" "参数二" "3"
param1:参数一
param2:参数二
param3:3

某些特殊参数只能被引用,不能改变值,这些参数是,@,#,?,-,$,!,0,_*

特殊参数*

引用特殊参数* 将输出从位置1开始的所有位置参数(有几个位置参数就输出几个单词),如果是在双引号内引用,如"$*“则输出一个包含所有位置参数的单词(多个位置参数合并为一个单词),此时每个单词中间用内部变量$IFS的第一个字符连接,如果变量IFS没有定义,则默认使用空格连接,如果为空”",则参数直接相连。

set one two three
#此时变量IFS未定义
echo $IFS

echo $*
one two three
#虽然输出结果和上一个看起来一样,但其实下面的是一个单词,中间包含空格"one two three"
echo "$*"
one two three
#验证上下两种引用的不同
for var in $*
> do
> echo $var
> done
one
two
three
for var in "$*"
> do
> echo $var
> done
one two three
特殊参数@

引用特殊参数@也将输出从位置1开始的所有位置参数,但是当在双引号内引用时,它的输出还是将多个参数认为是不同的多个参数,这点会在for循环调用中体现出来,例子可以参考上面,此处不再举例。

特殊参数#

引用特殊参数#将输出位置参数的个数,如下:

echo $#
3
特殊参数?

将输出最近一个在前台执行的命令的退出状态,命令正确执行没报错,退出状态返回0,否则将是其他数字,如下:

ll
total 1369052
...
-rwxrwxrwx. 1 root root         65 Aug 19 21:50 test.sh
...
echo $?
0
cat test1.sh
cat: test1.sh: No such file or directory
echo $?
1
特殊参数-

输出当前的选项标志,这个标志是调用时内部命令set指定,或者shell自身指定,和用户无关,了解即可

echo $-
himBH
特殊参数$

输出当前shell的进程号,在子shell中输出的是调用该子shell的进程号,而不是子shell的进程号

echo $$
24448
特殊参数0

输出当前shell或当前shell脚本的名称,在shell初始化时设置

#假设当前脚本test.sh内容如下
#!/bin/bash
echo "当前执行的脚本名称是:$0"
sh ./test.sh
当前执行的脚本名称是:./test.sh
特殊参数_

在shell启动时,设置为开始运行的shell或shell脚本的路径,随后输出前一个命令的最后一个参数,如下:

假设当前的test.sh脚本内容如下
#!/bin/bash
echo "当前\$_$_"
df -h
echo "运行完是$_"
下面运行脚本
bash ./test.sh
当前$_ 是 /usr/bin/bash
...省略df -h输出内容
运行完是-h

./test.sh
当前$_ 是 ./test.sh
...省略df -h输出内容
运行完是-h

declare指定变量类型

declare命令是Bash的内部命令,用于声明变量和修改变量的属性,它与Bash的另一个内部命令typeset用法和用途完全相同

declare

直接使用declare命令,不指定变量,将显示所有变量的值

declare
ABRT_DEBUG_LOG=/dev/null
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_COMPLETION_COMPAT_DIR=/etc/bash_completion.d
BASH_LINENO=()
BASH_REMATCH=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")
...
declare -r定义变量为只读

将指定变量定义为只读变量,这个变量不能被赋予新值或清除

declare -r qq=123
echo $qq
123
qq=234
-bash: qq: readonly variable
unset qq
-bash: unset: qq: cannot unset: readonly variable
declare -i定义变量为整数型

将指定变量定义为整数型变量,对该变量的任何赋值将会被转化为整数

declare -i var
var=1
echo ${var}
1
var="这下是个字符串"
-bash: 这下是个字符串: syntax error: operand expected (error token is "这下是个字符串")
var="this"
echo ${var}
0
var=10%4
echo ${var}
2
declare -x声明环境变量

指定的变量会成为环境变量,可供shell以外的程序来使用

declare -p显示指定变量的属性和值
接上文
declare -p var
输出:declare -i var="2"
declare -a声明数组

声明数组可以采用arrname=(v1 v2 v3)或者declare -a arrname=(v1 v2 v3)

数组属性可以通过declare声明,作用于数组每个成员

引用数组成员时,从下标0开始引用,${arr[0]}(花括号是必须要的) ,改变某个位置的值通过arr[i]=v来实现,如果下标是@或者*会引用所有的变量,不指定下标时输出第一个位置的元素

arr=(1 2 3)
echo ${arr[1]}
2
echo ${arr[*]}
1 2 3
echo ${arr[@]}
1 2 3
echo ${arr}
1
arr[2]=4
${arr[@]}
1 2 4
arr[4]=6
echo ${arr[*]}
1 2 4 6
echo ${arr[3]}

shell算术运算

shell可以进行算数运算,可以通过算数拓展或者通过内部命令let实现

bash运算符

bash运算符的优先级,结合性和值都与C语言相同,下面是优先级从高到低排列:

操作符用途
id++ id–后递增,后递减
++id --id前递增,前递减
-+单目负号和正号(用在数字前边)
!~逻辑取反,按位取反
**求n次方,如5**2=25
*、/、%乘、除、取余
±加减
<<、 >>按位左移,按位右移
<= 、>=、<、>比较符号
==、!=相等,不等
&、^、|按位与、按位异或、按位或
&&、||逻辑与、逻辑或
expr?expr:expr条件运算符
=、*=、/=、%=、+=、-=、<<=、>>=、&=、^=、|=赋值
expr1,expr2逗号运算,连接多个运算,只有最后一个运算值返回
数字常量

默认情况,shell运算采用十进制,除非数字有特定前缀标志,以0开头的常量将被当做八进制数解释,而以"0x"或"0X"开头的为十六进制数。如果数值格式是BASE#NUMBER,BASE是介于2-64之间的十进制数,表示算数进制技术,例如BASE为12,12#NUMBER表示12进制数,NUMBER是该进制下的数值。

let dec=20
echo "十进制数 dec=${dec}"
十进制数 dec=20
let oct=071
echo "八进制数 oct=${oct}"
八进制数 oct=57
let hex=0X91
echo "十六进制数 hex=${hex}"
十六进制数 hex=145
let bin=0110
echo "二进制数 bin=${bin}"
二进制数 bin=72
let bin=2#0110
echo "二进制数 bin=${bin}"
二进制数 bin=6
let base64=64#@_
echo "六十四进制数 base64=${base64}"
六十四进制数 base64=4031

在64进制中,0-9即使用0-9表示,10-35用a-z表示,36-61用A-Z表示,62和63分别用@和_表示

使用算术扩展和let进行算数运算

算数扩展只能运算整数,不能对浮点数进行算术运算

直接上示例:

var=5
var=$(($var+8))
echo $var
13
x=17
y=2
z=$(x%y)
-bash: x%y: command not found
z=$((x%y))
echo ${z}
1
#条件为真返回1,条件为假返回0
echo $((10>3))
1
a=28
b=25
echo $(($((a>b))?a:b))
28
echo $((a>b?a:b))
28

let也可以进行算数运算,默认情况下运算符左右两边不允许有空格,如果有空格,需要用双引号包起来

let i=i+5
echo $i
5
let i = i+5
-bash: let: =: syntax error: operand expected (error token is "=")
let "i = i+5"
echo $i
10
使用expr命令

expr命令用于对表达式进行求值并输出相应结果,只支持整数运算,不支持浮点数运算。与命令相反,使用该命令,运算符左右两边必须包含空格,如果没有空格,将不会求值而是直接输出算数表达式,有些运算符需要使用"\"进行转义,否则会提示语法错误(包括*、<、>、<=、>=、|、&)。通过expr给变量赋值时需要使用shell拓展中的命令替换

expr 6 + 8
14
expr 6+8
6+8
expr 6 * 8
expr: syntax error
expr 6 \* 8
48
a=5
b=6
#通过命令替换给C赋值
c=$(expr $a \* $b)
echo $c
30

退出脚本

exit命令用于结束并退出一个shell脚本

一个运行成功的命令会返回一个0,不成功会返回其他值,shell脚本及里面的函数也会返回一个退出状态码,在shell脚本或函数中,最后执行的一条命令决定其退出状态。通过特殊参数?可以得知退出状态码。

校验程序的退出状态码是有用且必要的(某些危险指令下),比如下面两条:

cd $DIR
rm -rf *

脚本的本意是切换到DIR目录下,删除该目录下所有文件,加入我们不对cd命令结果进行验证,假如该文件夹不存在,那么rm命令将在当前文件夹下执行,这可能产生不可预料的损失。

在shell脚本中,通过exit N命令可以用于提交一个退出状态码给shell(N必须是介于0-255之间的整数),如果省略了退出状态码N,则将把最后一条运行的命令的退出状态作为脚本的退出状态码。

结合退出状态码,对上面的脚本进行改写:

cd $DIR
if [$? -eq 0];then
rm -rf *
else
echo "找不到切换的文件夹"

exit 1
fi

调试脚本

通过bash -x xxx.sh可以调试脚本,该命令会启动子shell,以调试模式运行脚本,在执行过程中将实际执行的命令显示出来,其中的参数也是实际的运行时参数,每个命令行前面有个+号,如下例子中+号后面是实际运行的命令,其他是运行结果

bash -x test.sh
+ echo '当前$_ 是 /usr/bin/bash'
当前$_ 是 /usr/bin/bash
+ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G   28G   10G  74% /
devtmpfs        3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           3.9G   89M  3.8G   3% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
/dev/xvdb1       59G   56M   56G   1% /data-disk
tmpfs           783M     0  783M   0% /run/user/0
+ echo $'\350\277\220\350\241\214\345\256\214\346\230\257-h'
运行完是-h

在shell脚本中使用’"set -x"表示启动某选项,"set +x"表示关闭某选项,通过这两个命令可以只调试shell中的某一段脚本。比如我们先将前述test.sh脚本修改成如下内容:

#!/bin/bash
set -x
echo "当前\$_$_"
set +x
df -h
echo "运行完是$_"
#上述脚本中我们当前想调试的是这段脚本echo "当前\$_ 是 $_",执行后结果如下:
./test.sh
+ echo '当前$_ 是 -x'
当前$_ 是 -x
+ set +x
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G   28G   10G  74% /
devtmpfs        3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           3.9G   89M  3.8G   3% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
/dev/xvdb1       59G   56M   56G   1% /data-disk
tmpfs           783M     0  783M   0% /run/user/0
运行完是-h

bash中-v选项可以激活详细输出模式,通常调试是会将-v和-x配合使用,得到更详细的脚本信息

bash -xv test.sh
#!/bin/bash
echo "当前\$_$_"
+ echo '当前$_ 是 /usr/bin/bash'
当前$_ 是 /usr/bin/bash
df -h
+ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G   28G   10G  74% /
devtmpfs        3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           3.9G   89M  3.8G   3% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
/dev/xvdb1       59G   56M   56G   1% /data-disk
tmpfs           783M     0  783M   0% /run/user/0
echo "运行完是$_"
+ echo $'\350\277\220\350\241\214\345\256\214\346\230\257-h'
运行完是-h

使用-x虽然方便,但是没有显示代码行号等信息,下面几个bash内部环境变量配合起来可以更方便显示调试信息:

$LINENO:表示shell脚本的当前行号

$FUNCNAME:包含当前在执行调用堆栈中的所有shell函数名称的数组变量。${FUNCNAME[0]}表示当前正在执行的shell函数的名称,${FUNCNAME[1]}代表调用函数${FUNCNAME[0]}的函数名字,依次类推

$PS4:之前调试中每一行命令前的+号就是该变量的默认值

通过这几个变量配合,我们可以通过重新定义$PS4,增强-x输出的信息

此外还可以通过bash -n检查脚本是否有语法错误。

shell条件执行

条件测试

test命令测试成功返回0(真),失败返回1(假),test命令可以用于文件属性,字符串,算术测试,语法为test expression或[空格expression空格]

test -d "$HOME";echo $?
0
["abc" != "bcd"];echo $?
-bash: [abc: command not found
127
[ "abc" != "bcd" ];echo $?
0
test 7 -gt 3 && echo TRUE || echo FALSE
TRUE
常用文件属性测试操作符

-e:文件存在则为真

-f:存在且为常规文件则为真

-d:存在且是一个目录则为真

-r:存在且是可读的则为真

-w:存在且是可写的则为真

-x:存在且是可执行的则为真

-s:存在且不为空则为真

test -e /bin/cp && echo "The command $_ found" || echo "The command $_ not found."
The command /bin/cp found

命令中$_表示前一个执行的命令中的最后一个参数。

常用字符串操作符如下

-z:字符串为空则为真

-n:字符串不为空则为真

=(两字符串相等)!=、<(比较字典序)、>

由于大于小于在shell中也被用于重定向,因此比较字符串的时候需要加转义

test "abc" \< "def";echo $?
0
常用算数测试操作符

-eq:等于;-ne:不等于;-le:小于等于;-ge:大于等于;-lt:小于;-gt:大于

test 5 -eq 5 && echo YES || echo NO
YES
if结构语法格式

if语句的条件判断命令可以使用test命令,也可以是其他运行成功返回状态码0,失败返回其他状态码的命令,if语法格式如下;

if [ test-commands ]; then
  其他指令
fi
或者
if test-commands
then
  其他指令
fi
或者
if [ test-commands ]; then 其他指令;fi
if…else…fi语句

语法如下:

if [ test-commands ]
then
 其他指令
else
 其他指令(或者继续跟着if语句)
fi
还可以嵌套
if [ test-commands1 ]
then
 执行指令1
 if [ test-commands2 ]; 
 then
   执行指令2
 fi
else
 执行指令3
fi
if…elif…elif…fi
if [ test-commands1 ]
then
  其他指令
elif [ test-commands1 ]
then
   其他指令
elif [ test-commands1 ] 
then
  其他指令
else
  其他指令
fi

条件执行

bash下可以根据最后一个命令的退出状态使用条件执行来连接两个命令,也可以直接在if语句中使用条件执行,bash支持以下两种条件执行:

逻辑与—只有当前一个命令执行成功时才执行后一个命令

逻辑或—只有当前一个命令执行失败时才执行后一个命令

逻辑与&&

逻辑与&&用法为command1&&command2,只有当command1返回一个退出状态码0时,才会执行command2,也就是1执行成功才会执行2,前面已经有例子,此处不再举例。

也可以通过&&在if语句中将多个test命令连接在一起,比如:

var=123
if [ ${var} -ge 100 ] && [ ${var} -lt 150 ]
then
echo "haha"
fi

if [[ ${var} -ge 100 && ${var} -lt 150 ]]
then
echo "haha"
fi
#test命令中还可以使用-a表示逻辑与
if [ ${var} -ge 100 ] -a [ ${var} -lt 150 ]
then
echo "haha"
fi
if [ ${var} -ge 100 -a ${var} -lt 150 ]
then
echo "haha"
fi

需要注意的是使用-a代替&&有一点不同,-a会将前后两个test命令参数都拓展执行,但是&&只有前一个为真才执行后一个,假如两个命令前一个结果是假,后一个为echo语句,使用&&不会执行后面的输出语句,但使用-a会执行后面的输出语句,此外从可读性方面也不建议使用-a代替&&

逻辑或||

用法command1||command2,只有当command1返回假(非0)时才会运行command2,也就是只有command1执行失败才会执行command2

||可以与逻辑与&&联合使用,也可以多个逻辑或联合使用,因概念比较简单,不再举例

在逻辑或语句中可以使用-o代替||,但是从可读性,运行效率方面看也不建议这么做。

逻辑非!

用法!command用来测试真假,比较简单,不展开了。

case语句

case语句是多级if…then…else…fi语句的替代方式,可以让一个条件与多个模式比较,类似java中switch,case。语法如下:

case EXPRESSION in
PATTERN1)
  执行命令1;;
PATTERN2)
  执行命令2;;
PATTERN3)
  执行命令3
  ;;
esac
case $var in
123)
  echo "命中123";;
234)
  echo "命中234";;
345)
  echo "命中345"
  ;;
esac

case语句一定要以esac结尾,每一个命令列表都以两个分号";;“为终结,只有最后一个命令列表(即esac语句之前)的”;;"可以省略

bash循环

for循环语法
#基本语法
for var in {item1 item1 item1 ...itemN} 
do
    执行语句
done
#循环取变量内容语法
for var in $变量
do
执行语句
done
#for循环命令替换(去命令执行结果)的语法如下
for var in $(command)#或者for var in 'command'
do
执行语句
done
#也可以使用常见的循环
for (( i = 0; i < n; i++ ))
do
    
done
#同样可以嵌套循环
for i in {1..5}
do
  for i in {1..5} ; 
  do
  echo "haha"    
  done
    echo i
done
while循环
while [ condition ];
do
    其他指令
done

使用true,false:可以定义无限循环,如下

while true;
do
    echo "nihao"
done
while :
do
    echo "nihao"
done
until循环

until循环与while循环类似,也是基于一个条件,但其与while循环的逻辑正好相反,当条件被满足时退出循环,不满足时才持续运行。until循环语法如下:

until [ EXPRESSION ];
do
echo "你好"
done
#将test.sh修改为如下内容
#!/bin/bash
var=1
until [ ${var} -gt 3 ];
do
  echo "this is the ${var} time to print"
  var=$((${var}+1))
done
#执行
./test.sh
this is the 1 time to print
this is the 2 time to print
this is the 3 time to print
select循环

语法如下:

select var in list
do
command1
command2
...
commandn
done

select循环特点:

  • select语句使用bash内部变量PS3的值作为提示符
  • 打印到屏幕上的列表list当中的每一项会在前面加上数字编号
  • 当用户输入的数字与某一个数字编号一致是,列表中相应的项即被赋予变量var
  • 如果用户输入的内容为空,则重新显示列表list中的项和提示符信息
  • 可以添加一个退出选项,或者按ctrl+C或者ctrl+D组合键退出select循环
#test.sh
#!/bin/bash
myArray=(a b c d)
for i in ${myArray[*]} ;
do
 echo $i
done
PS3="please enter your choice:"
select item in ${myArray[*]};
do
  case $item in
  a)
   echo "您选择了a选项..."
    ;;
  b)
   echo "您选择了b选项..."
    ;;
  c)
   echo "您选择了c选项..."
    ;;
  d)
   echo "您选择了d选项...即将退出脚本"
   exit
    ;;
  esac
done
#执行脚本test.sh
./test.sh
a
b
c
d
1) a
2) b
3) c
4) d
please enter your choice:1
您选择了a选项...
please enter your choice:2
您选择了b选项...
please enter your choice:3
您选择了c选项...
please enter your choice:4
您选择了d选项...即将退出脚本

循环控制

break,continue用来进行循环控制,与其他语句含义一致。

break

用于从for、while、until或select循环中退出,停止,语法如下:break n

其中n代表嵌套循环的层级,指定了n,将退出n级嵌套循环,没有指定或者n小于1,则退出状态码为0,否则退出状态码为n

#退出两层嵌套循环
for i in {1..5}
do
  for i in {1..5} ; 
  do
  echo "haha" 
  break 2
  done
    echo i
done
continue

continue语句用于跳过循环体中剩余的命令直接跳转到循环体的顶部,重新开始循环的下一次重复,continue用于for、while或until循环

语法为continue n

shell函数

函数定义

函数的目的就是为了复用,函数定义语法如下:

function_name()
{
	#函数体
	command
	#参数返回,return语句可选,没有return语句,则以函数最后一条命令的运行结果作为返回值;如果使用return语句,则return后跟数值n(0-255)
	[return int]
}
#或者可在前面加上function关键字,有function可以省略圆括号()
function name()
{
	commands
}
#也可以在一行内定义一个函数,此时函数体内各命令之间必须用分号“;”隔开,语法如下
function name { command1;command2;commandN }
或name(){ command1;command2;commandN }

可以使用内部命令unset的"-f"选项来取消函数的定义,通常情况下函数体外的大括号与函数体之间必须用空白符(空格、回车或制表符等)或换行符分开。

函数的参数、变量与返回值

向函数传递参数

函数中使用参数的语法规则如下:

name(){
 arg1=$1
 arg1=$2
 //指令
}

使用如下语法调用函数:

#name-函数名;param1-参数1;param-参数2
name param1 param2

回顾下之前提到的位置参数:

  • 所有函数参数都可以通过$1,$2,…即位置参数来访问
  • $0指代脚本名称
  • $*或$@保存传递给函数的所有参数
  • $#保存传递给函数的参数个数
本地变量

默认情况下函数的变量为全局变量,在函数内改变变量值之后外面的变量值也会变化,local命令用来创建一个本地变量,其语法如下所示:

local var=value
local varName
或者
function name(){
    #定义一个本地变量
    local var=$1
    其他命令
}

local命令只能在函数内部使用;local命令将变量名的可见范围限制在函数内部

return命令

return语句可选,没有return语句,则以函数最后一条命令的运行结果作为返回值;如果使用return语句,则return后跟数值n(0-255)

函数调用

函数调用方式有多种,可以直接在shell命令行调用,或者脚本内部调用,或者从其他函数文件中调用,也可以递归调用。

在命令行中调用

可以通过直接输入函数的名字,来调用或引用函数

function_name

在脚本中调用

在脚本中定义并且调用函数的方法如下:

functionname(){
	函数体
}
调用函数
functionname

要在脚本中调用函数,首先要定义函数,并且保证位于调用此函数的语句之前,所以脚本对于变量和函数的定义尽量放在脚本最前面

从函数文件中调用函数

可以把所有的函数存储在一个函数文件中,或者把所有的函数加载到当前脚本或是命令行。加载函数文件中所有函数的语法如下,可以在命令行调用或者脚本文件中调用:

. /path/to/your/functions.sh

source命令也可以用于加载任何函数文件到当前shell脚本或者命令行,语法如下:source filename [arguments]

source functions.sh
source /path/to/your/functions.sh
source functions.sh param1=xxx param2=xxx
递归函数调用
#!/bin/bash
function fact(){
   local number=$1
   if [ $number -le 0 ];then #判断条件
     res=1
   else
     fact $((number-1)) #递归调用fact函数
     temp=$res			#存储该层级之前的阶乘值
     number=$number
     res=$((number*temp)) #当前层级和之前层级的总阶乘数
   fi
 }
fact $1
echo $res

bash下函数递归调用很慢,尽量避免

将函数放在后台运行

将函数放在后台运行如下,在脚本中调用函数:

functionname(){
	函数体
}
调用函数
functionname &
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值