Shell编程呈上来

247 阅读7分钟

shell简介

何为shell

shell是用户与操作系统之间的接口,可以解析并执行用户输入,然后返回用户结果的解析程序。与GUI类似,只不过GUI是图形化的更容易被人们使用,而shell是命令行形式的,需要用户输入指令。shell编程指的是shell脚本编程。

shell种类

  1. Bourne Shell(/usr/bin/sh或/bin/sh)

  2. Bourne Again Shell(/bin/bash)

  3. C Shell(/usr/bin/csh)

  4. ...

查看当前shell类型

[root@hangzhou01 ~]# echo $SHELL
/bin/bash

变量

变量范围

局部变量:只在创建该变量的shell生效

全局变量:在创建该变量的shell和从该shell派生出来的子shell生效,通过export关键字标志

变量类型

shell编程中最常用的类型是字符串和数组

字符串

shell的字符串既可以使用单引号,也可以使用双引号

  • 单引号内的内容会直接输出,双引号内的内容有变量会解析变量

  • 单引号的限制:单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;

test.sh脚本
#!/bin/sh
city="北京";
echo "中国首都是$city"
echo "中国首都是"$city""
echo "中国首都是'$city'"
echo '中国首都是$city'
echo '中国首都是"$city"'
echo '中国首都是'$city''     --第一个为'中国首都是'$city'', 所以解析了变量


[root@hangzhou01 ~]# ./test.sh 
中国首都是北京
中国首都是北京
中国首都是'北京'
中国首都是$city
中国首都是"$city"
中国首都是北京

验证:

[root@hangzhou01 ~]# city="北京";
[root@hangzhou01 ~]# echo $city'1001'
北京1001
[root@hangzhou01 ~]# echo $city"1001"
北京1001
[root@hangzhou01 ~]# echo $city1001

[root@hangzhou01 ~]# 
  • 字符串长度
  • 截取子字符串
#!/bin/sh
str="student of beijing";
echo ${#str}
echo ${str:3:4}

[root@hangzhou01 ~]# ./test1.sh 
18
dent

数组

shell仅支持一维数组,且未限定数组大小

  • 定义
数组名=(值1 值2 ... 值n)

##赋值方式一
arr=(1,2,3,4,5,6)
##赋值方式二
arr[0]=1
arr[1]=2
arr[2]=3
arr[3]=4
arr[4]=5
arr[5]=6
  • 读取
${数组名[下标]}

##读取一个值
${arr[5]}
##读取整个数组
${arr[@]}
  • 获取长度
##获取数组的长度
${#arr[@]}
##获取数组某个值的长度
${#arr[5]}

传递参数

脚本支持传递参数,在脚本内怎样获取参数呢?

$0 --脚本名称,包括脚本路径
$n --获取第n个参数,n>=1,1表示第一个参数,2表示第二个参数...
$@ --获取所有参数
$# --参数个数
$? --最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

test2.sh
#!/bin/bash
echo "脚本路径:$0"
echo "第一个参数:$3"
echo "第二个参数:$2"
echo "第三个参数:$3"
echo "所有参数:$@"
echo "参数个数:$#"
echo "最后一条指令执行状态:$?"
[root@hangzhou01 ~]# ./test2.sh 101 102 103 
脚本路径:./test2.sh
第一个参数:103
第二个参数:102
第三个参数:103
所有参数:101 102 103
参数个数:3
最后一条指令执行状态:0

运算符

算术运算符、关系运算符、布尔运算符、字符串运算符、文件测试运算符

算术运算符

shell不支持简单的算术运算符,需要借助expr等其它工具来实现

加法 `expr a + b`
减法 `expr a - b`
乘法 `expr a \* b`   --注意:乘法需要使用反斜杠才能实现
除法 `expr a / b`

test3.sh
#!/bin/bash
echo `expr $1 + $2`
echo `expr $1 - $2`
echo `expr $1 \* $2`
echo `expr $1 / $2`

[root@hangzhou01 ~]# ./test3.sh 10 9
19
1
90
1

这种更好用,推荐:$((表达式))

[root@hangzhou01 ~]# echo $((6+4))
10
[root@hangzhou01 ~]# echo $((6-4))
2
[root@hangzhou01 ~]# echo $((6*4))
24
[root@hangzhou01 ~]# echo $((6/4))
1
[root@hangzhou01 ~]# echo $((6%4))
2

关系运算符

关系运算符表达式需要使用[ ]包裹使用,表示检查的表达式是否为真。关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

-eq 检查相等
-ne 检查不相等
-gt 检查左边大于右边
-lt 检查左边小于右边
-ge 检查左边大于等于右边
-le 检查左边小于等于右边

test4.sh
#!/bin/bash
if [ $1 -eq $2 ];then
        echo "$1$2相等"
fi
if [ $1 -ne $2 ];then
        echo "$1$2不相等"
fi
if [ $1 -gt $2 ];then
        echo "$1大于$2"
fi
if [ $1 -lt $2 ];then
        echo "$1小于$2"
fi
if [ $1 -ge $2 ];then
        echo "$1大于等于$2"
fi
if [ $1 -le $2 ];then
        echo "$1小于等于$2"
fi

[root@hangzhou01 ~]# ./test4.sh 1 9
1与9不相等
1小于9
1小于等于9
[root@hangzhou01 ~]# ./test4.sh 9 9
9与9相等
9大于等于9
9小于等于9
[root@hangzhou01 ~]# ./test4.sh 20 9
20与9不相等
20大于9
20大于等于9

布尔运算符

!    取反
-o   或
-a   与

test5.sh
#!/bin/bash
if [ $1 -eq 5 ];then
        echo '$1 -eq 5为真'
fi
if [ ! $1 -eq 5 ];then
        echo '! $1 -eq 5为真'
fi
if [ $1 -eq 5 -o $2 -eq 6 ];then
        echo '$1 -eq 5 -o $2 -eq 6为真'
fi
if [ $1 -eq 5 -a $2 -eq 6 ];then
        echo '$1 -eq 5 -a $2 -eq 6为真'
fi

[root@hangzhou01 ~]# ./test5.sh 5 5
$1 -eq 5为真
$1 -eq 5 -o $2 -eq 6为真
[root@hangzhou01 ~]# ./test5.sh 6 5
! $1 -eq 5为真
[root@hangzhou01 ~]# ./test5.sh 5 6
$1 -eq 5为真
$1 -eq 5 -o $2 -eq 6为真
$1 -eq 5 -a $2 -eq 6为真

字符串运算符

=  检查字符串相等
!= 检查字符串不相等
-z 检查字符串长度为0
-n 检查字符串长度不为0
$  检查字符串为为空


test6.sh
#!/bin/bash
if [ $1 = $2 ];then
        echo '$1 = $2为真'
fi
if [ $1 != $2 ];then
        echo '$1 != $2为真'
fi
if [ -z $1 ];then
        echo '-z $1为真'
fi
if [ -n $1 ];then
        echo '-n $1为真'
fi
if [ $1 ];then
        echo '$1为真'
fi

[root@hangzhou01 ~]# ./test6.sh abc abcd
$1 != $2为真
-n $1为真
$1为真
[root@hangzhou01 ~]# ./test6.sh abc abc
$1 = $2为真
-n $1为真
$1为真

文件运算符

-d 检测文件是否是目录,如果是,则返回 true。
-f 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。
-r 检测文件是否可读,如果是,则返回 true。
-w 检测文件是否可写,如果是,则返回 true。
-x 检测文件是否可执行,如果是,则返回 true。
-s 检测文件是否为空(文件大小是否大于0),不为空返回 true。
-e 检测文件(包括目录)是否存在,如果是,则返回 true。

test7.sh
#!/bin/bash
if [ -f $1 ];then
        echo "$1是文件"
fi
if [ -d $1 ];then
        echo "$1是目录"
fi
if [ -r $1 ];then
        echo "$1可读"
fi
if [ -w $1 ];then
        echo "$1可写"
fi
if [ -x $1 ];then
        echo "$1可执行"
fi
if [ -s $1 ];then
        echo "$1不为空"
fi
if [ -e $1 ];then
        echo "$1存在"
fi

yaya.txt不为空,yaya1.txt为空文件,first为目录
[root@hangzhou01 ~]# ./test7.sh yaya.txt 
yaya.txt是文件
yaya.txt可读
yaya.txt可写
yaya.txt不为空
yaya.txt存在
[root@hangzhou01 ~]# ./test7.sh yaya1.txt 
yaya1.txt是文件
yaya1.txt可读
yaya1.txt可写
yaya1.txt存在
[root@hangzhou01 ~]# ./test7.sh first/
first/是目录
first/可读
first/可写
first/可执行
first/不为空
first/存在

流程控制

if

##if 语句语法格式:
if condition
then
    command1 
    command2
    ...
    commandN 
fi

##if-else 语句语法格式:
if condition
then
    command1 
    command2
    ...
    commandN
else
    command
fi

##if else-if else 语句语法格式:
if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi


test8.sh
#!/bin/bash
if [ $1 -lt 100 ];then
        echo "if 小于100"
fi
if [ $1 -lt 100 ];then
        echo "if-else 小于100"
else
        echo "if-else 大于等于100"
fi
if [ $1 -lt 100 ];then
        echo "if-else if-else 小于100"
elif [ $1 -eq 100 ];then
        echo "if-else if-else 等于100"
else
        echo "if-else if-else 大于100"
fi

[root@hangzhou01 ~]# ./test8.sh 99
if 小于100
if-else 小于100
if-else if-else 小于100
[root@hangzhou01 ~]# ./test8.sh 100
if-else 大于等于100
if-else if-else 等于100
[root@hangzhou01 ~]# ./test8.sh 199
if-else 大于等于100
if-else if-else 大于100
[root@hangzhou01 ~]# ./test8.sh 200
if-else 大于等于100
if-else if-else 大于100
[root@hangzhou01 ~]# ./test8.sh 299
if-else 大于等于100
if-else if-else 大于100

case

casein   --模式里面的;;不可少,表示跳出case,即最多只能匹配一个模式;如果模式为*,表示匹配所有值
模式1)
    command1
    command2
    ...
    commandN
    ;;                      
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

test13.sh
#!/bin/bash
case $1 in
        1) echo $1;
;;
        2) echo $1;
;;
        3) echo $1;
;;
        4) echo $1;
;;
        5) echo $1;
;;
        6) echo $1;
;;
        *) echo "默认匹配:$1";
esac

[root@hangzhou01 ~]# ./test13.sh 5
5
[root@hangzhou01 ~]# ./test13.sh 9
默认匹配:9

for

for var in item1 item2 ... itemN  --in的内容用空格隔开,in是可选的,如果无in,则for循环使用命令行的位置参数
do
    command1
    command2
    ...
    commandN
done

test9.sh
#!/bin/bash
for item in I am a student
do
        echo $item
done

[root@hangzhou01 ~]# ./test9.sh 
I
am
a
student


test10.sh
#!/bin/bash
for item
do
        echo $item
done

[root@hangzhou01 ~]# ./test10.sh 121 222 323 424 525
121
222
323
424
525

while

while condition --用于不断执行一系列命令
do
    command
done

while read line--用于从输入文件中读取数据
do
    command
done < 文件路径


test11.sh
#!/bin/bash
count=1;
while [ $count -le 100 ]
do
        count=`expr $count + 20`
        echo $count
done

[root@hangzhou01 ~]# ./test11.sh 
21
41
61
81
101


test12.sh
#!/bin/bash
while read line
do
        echo $line
done < ./input.txt

[root@hangzhou01 ~]# ./test12.sh 
I am a student ha ha!
I am in Beijing.
Chinese is good.

break和continue

break跳出循环

continue跳出本层循环

函数

定义

先定义后使用

[ function ] funname [()]

{

    action;

    [return int;]

}

--参数返回,可以显示加:return返回,return后跟数值n(0-255)。如果不加,将以最后一条命令运行结果,作为返回值。

调用函数

  • 无参调用
test14.sh
#!/bin/bash
function myPrint(){
        echo "输出了OK"
}
myPrint
echo $?

[root@hangzhou01 ~]# ./test14.sh 
输出了OK
0
test15.sh
#!/bin/bash
function myPrint(){
        echo "输出了OK"
        return 107
}
myPrint
echo $?

[root@hangzhou01 ~]# ./test15.sh 
输出了OK
107
  • 传参调用

    调用函数时可以向其传递参数。在函数体内部,通过 n的形式来获取参数的值,例如,n 的形式来获取参数的值, 例如,1表示第一个参数,2表示第二个参数...以及2表示第二个参数...以及@、$*等其他的位置参数

    test16.sh #!/bin/bash function myPrint(){ echo "输出第一个参数:1"echo"输出第二个参数:1" echo "输出第二个参数:2" } myPrint 11 2

    [root@hangzhou01 ~]# ./test16.sh 中国 北京 输出第一个参数:中国 输出第二个参数:北京

    --请注意函数体里面的位置参数与函数体外面位置参数的区别

重定向

核心概念

一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:

  • 标准输入文件(stdin):文件描述符为0,Unix程序默认从stdin读取数据。
  • 标准输出文件(stdout):文件描述符为1,Unix程序默认向stdout输出数据。
  • 标准错误文件(stderr):文件描述符为2,Unix程序会向stderr流中写入错误信息。

默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。

输出重定向

用指定的输出文件来替换标准输出或者标准错误输出。有一个特殊的输出文件/dev/null ,如果重定向到该文件,则表明遗弃输出。

command > file   将命令输出从标准输出以覆盖方式重定向到 file
command >> file  将命令输出从标准输出以追加方式重定向到 file
n>&m             将文件描述符n重定向到文件描述符m


who >file  标准输出重定向到file,等价于who 1>file 
who >>file 
who 2>file  标准错误输出重定向到file
who 2>>file 
who >>file 2>>fileErr  标准输出重定向到file,标准错误输出重定向到fileErr  
who >>file 2>>&1       标准输出重定向到file,标准错误输出重定向到标准输入 --建议使用
who >>file 2>>file     标准输出重定向到file,标准错误输出重定向file --不建议使用,会打开两次file

输入重定向

用指定的输入文件来替换标准输入

command < file   将标准输入重定向file