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

参考文章:

[1]Linux中编写Shell脚本

[2]shell语法

原文地址:https://www.cnblogs.com/zyly/p/15323917.html