Linux shell脚本编写基础
一、shell入门
1.1 介绍
shell是一个命令解释器,它的作用是解释执行用户输入的命令以及程序等。用户每输入一条命令,shell就执行一条。这种从键盘输入命令,就可以立即得到回应的对话方式,称为交互的方式。
当命令或程序语句不在命令行下执行,而是通过一个程序文件来执行时,该程序文件就被称为shell脚本。 在shell脚本里内置了很多命令、语句及循环控制,然后将这些命令一次性执行完毕,这种通过文件执行脚本的方式称为非交互的方式。
1.2 shell脚本类型
shell脚本语言是弱类型语言,无需定义变量类型即可使用,在unix/linux中主要有两大类shell:
- Bourne shell:包括Bourne shell(sh)、Korn shell(ksh)、Bourne again shell;
- C shell:包括csh、tcsh两种类型;
查看系统默认的shell:echo \$SHELL
查看系统支持的shell:cat /etc/shells
[root@bogon194 ~]# echo $SHELL
/bin/bash
[root@bogon194 ~]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
[root@bogon194 ~]#
1.3 shell脚本的执行
shell脚本的执行通常可以采用以下几种方式:
- bash filename或sh filename:这是当脚本文件本身没有执行权限时常使用的方法;
- /path/filename或./filename:当前路径下执行脚本,需要将脚本文件的权限改为可执行。然后使用脚本的绝对路径或相对路径就可以直接执行脚本了;
- source filename或.filename: 这种方法通常是使用source或 “.”(点号)读入或加载指定的shell脚本文件,然后依次执行指定的Shell脚本中的语句。这些语句将在当前父 shell 脚本进程中执行(其他几种模式都会启用新的进程执行该脚本进程)。
1.4 shell脚本编写规范
shell脚本的开发规范及习惯非常重要,虽然这些规范不是必须要遵守的,但是有了好的规范和习惯,可以大大提高开发效率,并能在后期降低对脚本的维护成本。
- 一个规范的shell脚本在第一行会指出由那个解释器来执行脚本中的内容,这一行内容在linux bash的编程一般为:#! /bin/bash 或 #! /bin/sh,bash和sh的区别,sh为bash的软连接,大多数情况下,脚本使用:#! /bin/bash 和 #! /bin/sh是没有区别的。
- 在shell脚本中,跟在 # 后面的内容表示注释,用来对脚本进行注释说明,注释部分不会被当做程序来执行,仅仅是给开发者和使用者看的,系统解释器是看不到的,更不会执行。注释可以自成一行,也可以跟在脚本命令的后面与命令在同一行。 注释尽量不要使用中文,在脚本中最好也不要有中文。
- shell脚本的开头加版本、版权等信息。
- shell脚本的命名应以.sh为扩展名。
- 成对的符号应尽量一次性写出来,然后退格在符号内增加内容,以防止遗漏。这些成对的符号包括: {}、[]、‘’、“” 等。
- 中括号[]两端至少要有1个空格,因此,键入中括号时即留出空格[ ],然后在退格键入中间内容,并确保两端都至少由一个空格。
- 对于流程控制语句,应一次性将格式写完,再添加内容。 如:一次性完成for循环语句的格式:
for
do
内容
done
- 通过缩进让代码更易读,如:
if 条件内容
then
内容
fi
- 脚本中的单引号、双引号及反引号必须为英文状态下的符号。
二、变量
简单地说,变量就是用一个固定的字符串(也可能是字符、数字等的组合)代替更多、更复杂的内容,该内容里可能还会包含变量、路径、字符串等其他内容。 变量是暂时存储数据的地方及数据标记,所存储的数据存在于内存空间中,通过正确地调用内存中变量的名字就可以读取出与变量对应的数据。
变量的赋值方法为: 先写变量名称,紧接着是= ,最后是值。中间无任何空格。 通过echo命令加上 \$变量名,即可输出变量的值。
定义变量时变量名建议用大写,如 A=zy B=99;
read -p “提示信息” 变量名 #交互式赋值方法
查看变量内容echo \$A 或 echo \${A}:
[root@bogon194 shell]# A=zy
[root@bogon194 shell]# B=99
[root@bogon194 shell]# echo $A
zy
[root@bogon194 shell]# echo ${A}
zy
[root@bogon194 shell]# read -p "please input C:" C
please input C:9999
[root@bogon194 shell]# echo $C
9999
2.1 赋值时使用引号
- 双引号:允许通过$符号引用其它变量的值;
- 单引号:禁止引用其它变量的值,$视为普通字符;
- ``:命令替换,提取命令执行后的输出结果;
- $():同``;
[root@bogon194 shell]# A=10
[root@bogon194 shell]# echo $A
10
[root@bogon194 shell]# B=$A+10
[root@bogon194 shell]# echo $B
10+10
[root@bogon194 shell]# C="$A+20"
[root@bogon194 shell]# echo $C
10+20
[root@bogon194 shell]# D='$A+30'
[root@bogon194 shell]# echo $D
$A+30
[root@bogon194 shell]# E=`ls`
[root@bogon194 shell]# echo $E
shell.sh
需要注意的是shell 原生bash不支持简单的数学运算,所说义\$A+10为10+10。
命令替换(``、$())与变量替换(${})差不多,都是用来重组命令行的,先完成引号里的命令行,然后将其结果替换出来,再重组成新的命令行。
2.2 位置参数
位置参数是一种在调用 shell 程序的命令行中按照各自的位置决定的变量,是在程序名之后输入的参数。 位置参数之间用空格分隔,shell取第一个位置参数替换程序文件中的 \$1,第二个替换 \$2 , 依次类推。\$0 是一个特殊变量,它的内容是当前这个shell程序的文件名,所以 \$0 不是一个位置参数。
假如我现在有一个 1.sh脚本文件,内容如下:
#! /bin/bash
echo $0
echo $1
echo $(($2+$3))
当我执行时,我在文件名后加3个参数:
[root@bogon194 shell]# sh 1.sh 5 10 20
1.sh
5
30
这里的数值运算可以先忽略,后面会介绍。
2.3 预定义变量
预定义变量和环境变量相类似,也是在shell一开始就定义的变量,不同的是,用户只能根据shell的定义来使用这些变量,所有预定义变量都是由符号“\$”和另一个符号组成。 常见的Shell预定义变量有以下几种。
- \$# :位置参数的数量;
- \$* :所有位置参数的内容;
- \$? :命令执行后返回的状态,0表示没有错误,非0表示有错误;
- \$\$ :当前进程的进程号;
- \$! :后台运行的最后一个进程号;
- \$0 :当前执行的进程名(即shell程序的文件名);
- \$@ 传递给脚本或函数的所有参数。被双引号" "包含时,与 \$* 稍有不同,下面将会讲到。
假如我现在有一个 2.sh脚本文件,内容如下:
#!/bin/bash
echo "File Name: $0"
echo "First Parameter : $1"
echo "First Parameter : $2"
echo "Quoted Values: $@"
echo "Quoted Values: $*"
echo "Total Number of Parameters : $#"
当我执行时:
$sh 2.sh Zara Ali
File Name : 2.sh
First Parameter : Zara
Second Parameter : Ali
Quoted Values: Zara Ali
Quoted Values: Zara Ali
Total Number of Parameters : 2
\$* 和 \$@ 都表示传递给函数或脚本的所有参数,不被双引号" "包含时,都以"\$1" "\$2" … "\$n" 的形式输出所有参数。 但是当它们被双引号" "包含时,"\$*" 会将所有的参数作为一个整体,以"\$1 \$2 … \$n"的形式输出所有参数;"\$@" 会将各个参数分开,以"\$1" "\$2" … "\$n" 的形式输出所有参数。
下面的例子可以清楚的看到 \$* 和 \$@ 的区别:
#!/bin/bash
echo "\$*=" $*
echo "\"\$*\"=" "$*"
echo "\$@=" $@
echo "\"\$@\"=" "$@"
echo "print each param from \$*"
for var in $*
do
echo "$var"
done
echo "print each param from \$@"
for var in $@
do
echo "$var"
done
echo "print each param from \"\$*\""
for var in "$*"
do
echo "$var"
done
echo "print each param from \"\$@\""
for var in "$@"
do
echo "$var"
done
执行 sh 2.sh "a" "b" "c" "d",看到下面的结果:
$*= a b c d
"$*"= a b c d
$@= a b c d
"$@"= a b c d
print each param from $*
a
b
c
d
print each param from $@
a
b
c
d
print each param from "$*"
a b c d
print each param from "$@"
a
b
c
d
三、算术运算
shell中常见的算术运算符:
算术运算符 | 意义 |
+、-、*、/、% | 加法、减法、乘法、除法、取余 |
** | 幂运算 |
++、-- | 增加及减少 |
!、&&、|| | 逻辑非(取反)、逻辑与(and)、逻辑或(or) |
<、<=、>、>= | 比较符号(小于、小于等于、大于、大于等于) |
==、!= | 比较符号(相等、不等于) |
=、+=、-= | 赋值运算符、例如a+=1等价于a=a+1 |
shell中常见的算数运算命令:
运算操作符及运算命令 | 意义 |
(()) | 用于整数运算的常见运算符、效率很高 |
let | 用于整数运算、类似于(()) |
expr | 可用于整数运算,但还有很多其他功能 |
bc | linux下的一个计算器程序 |
$[] | 用于整数运算 |
3.1 (())数值运算命令的用法
双小括号(())的作用是进行数值运算与数值比较,它的效率很高,用法灵活,其操作方式如下:
运算操作符与运算命令 | 意义 |
((i=i+1)) | 运算后赋值,即将i+1的运算结果赋值给变量i。注意,不能用echo ((i=i+1))的形式输出表达式的值,但可以使用echo $((i=i+1))输出其值 |
i=$(($A+5)) | 可以在(())前加$,表示将表达式$A+5与运算后的值赋值给i |
((8>7&&5==5)) | 可以进行比较操作,还可以加入逻辑与和逻辑或,用于条件判断,if ((8>7&&5==5)) |
echo $((2+1)) | 需要直接输出运算表达式的运算结果时,可以在(())前加$ |
3.2 let运算符的用法
let运算命令的语法格式为: let 赋值表达式 ;
let 赋值表达式的功能等同于“((赋值表达式))” ;
[root@bogon194 shell]# i=1
[root@bogon194 shell]# let i=i+1
[root@bogon194 shell]# echo $i
2
3.3 expr命令的用法
expr用于计算:
语法:expr 表达式
示例:expr 2 + 2、expr 2 - 2 、expr 2 / 2
注意:运算符号和数字之间要有空格!!
[root@bogon194 shell]# expr 2 + 2
4
[root@bogon194 shell]# expr 2 - 2
0
[root@bogon194 shell]# expr 2 \* 2
4
[root@bogon194 shell]# expr 2 / 2
1
expr配合变量进行计算:
expr在shell中可配合变量进行计算,但需要用反引号将计算表达式括起来。
[root@bogon194 shell]# i=5
[root@bogon194 shell]# i=`expr $i + 5`
[root@bogon194 shell]# echo $i
10
利用expr计算字符串的长度:
[root@bogon194 shell]# char="hello"
[root@bogon194 shell]# expr length $char
5
3.4 bc命令的用法
bc 是UNIX/Linux下的计算器,除了作为计算器来使用,还可以作为命令行计算工具使用。
3.5 $[]运算符
$[]用于整数运算,示例如下:
[root@bogon194 shell]# echo $[2-5]
-3
[root@bogon194 shell]# echo $[2+5]
7
四、条件测试
通常,在shell的各种条件结构和流程控制结构中都要进行各种测试,然后根据测试结果执行不同的操作,有时候也会与 if 等条件语句相结合,来完成测试判断,以减少程序运行错误。
几种条件测试语句:
条件测试语法 | 说明 |
test 测试表达式 | 利用test命令进行条件表达式测试 |
[测试表达式] | 通过[]进行条件表达式测试 |
[[测试表达式]] | 通过[[]]进行条件表达式测试 |
((测试表达式)) | 通过(())进行条件表达式测试,一般用于if语法 |
测试表达式 test 、[] 、[[]] 、 (()) 的区别:
测试表达式符号 | test | [] | [[]] | (()) |
边界是否需要空格 | 需要 | 需要 | 需要 | 不需要 |
逻辑操作符 | ! 、-a、 -o | ! 、-a、 -o | ! 、&& 、 || | ! 、&& 、 || |
整数比较操作符 | -eq 、 -gt 、-lt、-ge 、-le | -eq 、 -gt 、-lt、-ge 、-le | -eq 、 -gt 、-lt、-ge 、-le 或 = 、> 、< 、 >= 、 <= | = 、> 、< 、 >= 、 <= |
字符串比较操作符 | = 、 == 、!= | = 、 == 、!= | = 、 == 、!= | 不支持 |
文件操作 | -d、-f、-e、-r、-s、-w、-x、-L、-nt、-ot | -d、-f、-e、-r、-s、-w、-x、-L、-nt、-ot | -d、-f、-e、-r、-s、-w、-x、-L、-nt、-ot | 不支持 |
是否支持通配符匹配 | 不支持 | 不支持 | 不支持 | 不支持 |
4.1 文件测试操作符
常用文件测试操作符 | 说明 |
-d , d的全拼为 directory | 文件存在且为目录则为真 |
-f , f的全拼为 file | 文件存在且为文件则为真 |
-e , e的全拼为 exists | 文件存在则为真 |
-s ,s的全拼为 size | 文件存在且大小不为0则为真 |
-r ,r的全拼为 read | 文件存在且可读则为真 |
-w ,w的全拼为write | 文件存在且可写则为真 |
-x ,x的全拼为executable | 文件存在且可执行则为真 |
-L ,L的全拼为link | 文件存在且为链接文件则为真 |
f1 -nt f2 ,nt的全拼为 newer than | 文件f1比文件f2新则为真 |
f1 -ot f2 ,ot的全拼为older than | 文件f1比文件f2旧则为真 |
test测试:
[root@bogon194 shell]# test -d 1.sh && echo true || echo false
false
[root@bogon194 shell]# test -f 1.sh && echo true || echo false
true
[root@bogon194 shell]# test -e 1.sh && echo true || echo false
true
[]测试:
[root@bogon194 shell]# [ -d 1.sh ] && echo true || echo false
false
[root@bogon194 shell]# [ -f 1.sh ] && echo true || echo false
true
[[]]测试:
[root@bogon194 shell]# [[ -d 1.sh ]] && echo true || echo false
false
[root@bogon194 shell]# [[ -f 1.sh ]] && echo true || echo false
true
4.2 字符串测试操作符
常用字符串测试操作符 | 说明 |
-n | 若字符串长度不为0,则为真 |
-z | 若字符串长度为0,则为真 |
“字符串1” == “字符串2” | 若字符串1等于字符串2,则为真 |
“字符串1” != “字符串2” | 若字符串1不等于字符串2,则为真 |
注: == 和 != 两端要有空格 。
test测试:
[root@bogon194 shell]# test "zy" == "zy" && echo true || echo false
true
[root@bogon194 shell]# test -n "zy" && echo true || echo false
true
[]测试:
[root@bogon194 shell]# [ "zy" == "zy" ] && echo true || echo false
true
[root@bogon194 shell]# [ -n "zy" ] && echo true || echo false
true
[[]]测试:
[root@bogon194 shell]# [[ "zy" == "zy" ]] && echo true || echo false
true
[root@bogon194 shell]# [[ -n "zy" ]] && echo true || echo false
true
4.3 整数二元比较操作符
在[]以及test中使用的比较符号 |
在(())和[[]]中使用的比较符号 |
说明 |
-eq | ==或= | 相等,全拼为 equal |
-ne | != | 不相等,全拼为 not equal |
-gt | > | 大于,全拼为 greater than |
-ge | >= | 大于等于,全拼为 greater equal |
-lt | < | 小于,全拼为 less than |
-le | <= | 小于等于 ,全拼为less equal |
- "="和"!="也可以在[]中作比较使用,但在[]中使用包含"<"和">"的符号时,需要用反斜线转义,有时不转义虽然语法不会报错,但是结果可能不对;
- 可以在[[]]中使用包含“-gt”和“-lt”的符号,但是不建议使用;
- 比较符号两端也要有空格,[] (()) [[]] 两端都要有空格;
test使用:
[root@bogon194 shell]# test 3 -eq 3 && echo true || echo false
true
[root@bogon194 shell]# test 3 -eq 4 && echo true || echo false
false
[]使用:
[root@bogon194 shell]# [ 3 -eq 3 ] && echo true || echo false
true
[root@bogon194 shell]# [ 3 -eq 4 ] && echo true || echo false
false
[[]]使用:
[root@bogon194 shell]# [[ 3 == 3 ]] && echo true || echo false
true
[root@bogon194 shell]# [[ 3 == 4 ]] && echo true || echo false
false
(())使用:
[root@bogon194 shell]# (( 3 == 3 )) && echo true || echo false
true
[root@bogon194 shell]# (( 3 == 4 )) && echo true || echo false
false
4.4 逻辑操作符
在[]和test中使用的操作符 | 在[[]]和(())中使用的操作符 | 说明 |
-a | && | and ,与,两端都为真,才为真 |
-o | || | or ,或, 两端有一个为真,就为真 |
! | ! | not ,非, 两端相反,则结果为真 |
4.5 if条件判断语句
简单条件判断:
#####单条件判断##############
if 条件判断
then
命令
else
命令
fi
#或
if 条件判断;then
命令
else
命令
fi
案例如下3.sh:
#! /bin/bash
read -p "please input number:" i
if [ $i -gt 5 ]
then
echo true
else
echo false
fi
执行:
[root@bogon194 shell]# sh 3.sh 9
please input number:12
true
双重条件判断:
###双条件判断#####
if 条件判断
then
命令
elif 条件判断
then
命令
else
命令
fi
##或
if 条件判断;then
命令
elif 条件判断;then
命令
else
命令
fi
4.6 case条件判断语句
case条件语句相当于多分支的if/elif/ellse条件语句,但是它比这些条件语句看起来更规范更工整,常被应用于实现系统服务启动脚本等企业应用场景中。
case 变量 in
one)
命令
;;
two)
命令
;;
*)
命令
esac
测试用例4.sh:
read -p "please input a number or str:" i
case $i in
[1-9])
echo "this a number"
;;
[a-z])
echo "this is a litter str"
;;
*)
echo "this is a dont known"
esac
执行结果:
[root@bogon194 shell]# sh 4.sh
please input a number or str:2
this a number
[root@bogon194 shell]# sh 4.sh
please input a number or str:d
this is a litter str
[root@bogon194 shell]# sh 4.sh
please input a number or str:&
this is a dont known
4.7 for循环语句
语法格式:
for 条件
do
命令
done
##或
for 条件;do
命令
done
4.8 while循环语句
语法格式:
while 条件
do
命令
done
4.9 break、continue、exit 循环控制语句
break 、continue在条件语句及循环语句(for、while、if等)中用于控制程序走向;而exit则用于终止所有语句并退出当前脚本。
命令 | 说明 |
break n | 如果省略 n ,则表示跳出整个循环,n 表示跳出循环的成熟 |
continue n | 如果省略 n ,则表示跳过本次循环,忽略本次循环的剩余代码,进行循环的下一次循环。n表示退到第 n 层继续循环 |
exit n | 退出当前 shell 程序,n 为上一次程序执行的状态返回值。n 也可以省略,在下一个 shell 里可通过 $? 接收 exit n 的n值 |
下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,就要使用break命令。
#!/bin/bash
while :
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "Your number is $aNum!"
;;
*) echo "You do not select a number between 1 to 5, game is over!"
break
;;
esac
done
在嵌套循环中,break 命令后面还可以跟一个整数,表示跳出第几层循环。例如:
break n
表示跳出第 n 层循环。
下面是一个嵌套循环的例子,如果 var1 等于 2,并且 var2 等于 0,就跳出循环:
#!/bin/bash
for var1 in 1 2 3
do
for var2 in 0 5
do
if [ $var1 -eq 2 -a $var2 -eq 0 ]
then
break 2
else
echo "$var1 $var2"
fi
done
done
如上,break 2 表示直接跳出外层循环。运行结果:
1 0
1 5
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
对上面的例子进行修改:
#!/bin/bash
while :
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "Your number is $aNum!"
;;
*) echo "You do not select a number between 1 to 5!"
continue
echo "Game is over!"
;;
esac
done
运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句:
echo "Game is over!"
永远不会被执行。
同样,continue 后面也可以跟一个数字,表示跳出第几层循环。
#!/bin/bash
NUMS="1 2 3 4 5 6 7"
for NUM in $NUMS
do
Q=`expr $NUM % 2`
if [ $Q -eq 0 ]
then
echo "Number is an even number!!"
continue
fi
echo "Found odd number"
done
运行结果:
Found odd number
Number is an even number!!
Found odd number
Number is an even number!!
Found odd number
Number is an even number!!
Found odd number
五、数组
在shell中,用括号来表示数组,数组元素用“空格”符号分割开。定义数组的一般形式为:
array_name=(value1 ... valuen)
例如:
array_name=(value0 value1 value2 value3)
还可以单独定义数组的各个分量:
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
可以不使用连续的下标,而且下标的范围没有限制。
读取数组元素值的一般格式是:
${array_name[index]}
例如:
valuen=${array_name[2]}
获取数组长度的方法与获取字符串长度的方法相同,例如:
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}
六、echo
echo是Shell的一个内部指令,用于在屏幕上打印出指定的字符串。命令格式:
echo arg
6.1 显示转义字符
echo "\"It is a test\""
输出:
"It is a test"
6.2 显示变量
name="OK"
echo "$name It is a test"
输出:
OK It is a test
同样双引号也可以省略。
如果变量与其它字符相连的话,需要使用大括号{ }:
mouth=8
echo "${mouth}-1-2009"
结果将是:
8-1-2009
6.3 显示结果重定向至文件
echo "It is a test" > myfile
6.4 原样输出字符串
若需要原样输出字符串(不进行转义),请使用单引号。例如:
echo '$name\"'
6.5 显示命令执行结果
echo `date`
七 函数
函数可以让我们将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高。像其他编程语言一样,shell 也支持函数。shell 函数必须先定义后使用。
7.1 函数定义
shell 函数的定义格式如下:
function_name () {
list of commands
[ return value ]
}
函数返回值,可以显式增加return语句;如果不加,会将最后一条命令运行结果作为返回值。
shell 函数返回值只能是整数,一般用来表示函数执行成功与否,0表示成功,其他值表示失败。如果 return 其他数据,比如一个字符串,往往会得到错误提示:“numeric argument required”。
如果一定要让函数返回字符串,那么可以先定义一个变量,用来接收函数的计算结果,脚本在需要的时候访问这个变量来获得函数返回值。
先来看一个例子:
#!/bin/bash
# Define your function here
Hello () {
echo "Url is http://see.xidian.edu.cn/cpp/shell/"
}
# Invoke your function
Hello
调用函数只需要给出函数名,不需要加括号。
再来看一个带有return语句的函数:
#!/bin/bash
funWithReturn(){
echo "The function is to get the sum of two numbers..."
echo -n "Input first number: "
read aNum
echo -n "Input another number: "
read anotherNum
echo "The two numbers are $aNum and $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
# Capture value returnd by last command
ret=$?
echo "The sum of two numbers is $ret !"
运行结果:
The function is to get the sum of two numbers...
Input first number: 25
Input another number: 50
The two numbers are 25 and 50 !
The sum of two numbers is 75 !
像删除变量一样,删除函数可以使用 unset 命令,不过要加上 .f 选项,如下所示:
$unset .f function_name
如果你希望直接从终端调用函数,可以将函数定义在主目录下的 .profile 文件,这样每次登录后,在命令提示符后面输入函数名字就可以立即调用。
7.2 函数传参
在shell中,调用函数时可以向其传递参数。在函数体内部,通过 \$n 的形式来获取参数的值,例如,\$1表示第一个参数,\$2表示第二个参数...
带参数的函数示例:
#!/bin/bash
funWithParam(){
echo "The value of the first parameter is $1 !"
echo "The value of the second parameter is $2 !"
echo "The value of the tenth parameter is $10 !"
echo "The value of the tenth parameter is ${10} !"
echo "The value of the eleventh parameter is ${11} !"
echo "The amount of the parameters is $# !" # 参数个数
echo "The string of the parameters is $* !" # 传递给函数的所有参数
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
运行脚本:
The value of the first parameter is 1 !
The value of the second parameter is 2 !
The value of the tenth parameter is 10 !
The value of the tenth parameter is 34 !
The value of the eleventh parameter is 73 !
The amount of the parameters is 12 !
The string of the parameters is 1 2 3 4 5 6 7 8 9 34 73 !"
注意,\$10 不能获取第十个参数,获取第十个参数需要\${10}。当n>=10时,需要使用\${n}来获取参数。
八、输入输出重定向
Unix 命令默认从标准输入设备(stdin)获取输入,将结果输出到标准输出设备(stdout)显示。一般情况下,标准输入设备就是键盘,标准输出设备就是终端,即显示器。
8.1 输出重定向
命令的输出不仅可以是显示器,还可以很容易的转移向到文件,这被称为输出重定向。
命令输出重定向的语法为:
command > file
这样,输出到显示器的内容就可以被重定向到文件。
例如,下面的命令在显示器上不会看到任何输出:
who > users
打开 users 文件,可以看到下面的内容:
[root@bogon194 shell]# cat users
root pts/0 2021-09-28 15:17 (10.80.12.52)
root pts/2 2021-09-28 16:26 (10.80.12.52)
输出重定向会覆盖文件内容,请看下面的例子:
[root@bogon194 shell]# echo line 1 > users
[root@bogon194 shell]# cat users
line 1
如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾。
8.2 输入重定向
和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:
command < file
这样,本来需要从键盘获取输入的命令会转移到文件读取内容。
注意:输出重定向是大于号(>),输入重定向是小于号(<)。
例如,计算 users 文件中的行数,可以使用下面的命令:
[root@bogon194 shell]# wc -l users
1 users
也可以将输入重定向到 users 文件:
[root@bogon194 shell]# wc -l < users
1
注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。
8.3 重定向深入讲解
一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
- 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
- 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
- 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
如果希望 stderr 重定向到 file,可以这样写:
command 2 > file
如果希望 stderr 追加到 file 文件末尾,可以这样写:
command 2 >> file
2 表示标准错误文件(stderr)。
如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:
command > file 2>&1
或
command >> file 2>&1
这里的&没有固定的意思,放在>后面的&,表示重定向的目标不是一个文件,而是一个文件描述符。换言之 2>1 代表将stderr重定向到当前路径下文件名为1的regular file中,而2>&1代表将stderr重定向到文件描述符为1的文件(即/dev/stdout)中,这个文件就是stdout在file system中的映射。
如果希望对 stdin 和 stdout 都重定向,可以这样写:
command < file1 >file2
command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。
命令 | 说明 |
---|---|
command > file | 将输出重定向到 file。 |
command < file | 将输入重定向到 file。 |
command >> file | 将输出以追加的方式重定向到 file。 |
n > file | 将文件描述符为 n 的文件重定向到 file。 |
n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
n >& m | 将输出文件 m 和 n 合并。 |
n <& m | 将输入文件 m 和 n 合并。 |
<< tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |
8.4 /dev/null 文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
command > /dev/null
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到”禁止输出“的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写:
command > /dev/null 2>&1
九、xargs
xargs(英文全拼: eXtended ARGuments)是给命令传递参数的一个过滤器,也是组合多个命令的一个工具。
xargs 可以将管道或标准输入(stdin)数据转换成命令行参数,也能够从文件的输出中读取数据。
xargs 也可以将单行或多行文本输入转换为其他格式,例如多行变单行,单行变多行。
xargs 默认的命令是 echo,这意味着通过管道传递给 xargs 的输入将会包含换行和空白,不过通过 xargs 的处理,换行和空白将被空格取代。
xargs 是一个强有力的命令,它能够捕获一个命令的输出,然后传递给另外一个命令。
之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要,所以就有了 xargs 命令,例如:
find /sbin -perm +700 |ls -l #这个命令是错误的
find /sbin -perm +700 |xargs ls -l #这样才是正确的
xargs 一般是和管道一起使用。
somecommand |xargs -item command
xargs 用作替换工具,读取输入数据重新格式化后输出。
定义一个测试文件,内有多行文本数据:
# cat test.txt
a b c d e f g
h i j k l m n
o p q
r s t
u v w x y z
多行输入单行输出:
# cat test.txt | xargs
a b c d e f g h i j k l m n o p q r s t u v w x y z
参考文章:
[2]shell语法