Shell学习
一、第一个Shell脚本
#!/bin/bash echo"Hello World"
#! 是一种约定的标记,告诉系统使用什么解释器执行;
执行Shell脚本主要有两种方式:
#第一种: #使用chmod命令,给脚本添加可执行权限 chmod +x ./hello.sh #执行脚本 ./hello.sh
#第二种:作为解释器的参数运行脚本呢 /bin/bash hello.sh #或者 /bin/sh hello.sh
ps:使用第二种方式就不需要在脚本的第一行标记使用的解释器了
以 # 开头的行就是注释,会被解释器忽略。
Shell中还可以使用多行注释,格式如下:
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
EOF 也可以使用其他符号代替,例如:
:<<' 注释内容... 注释内容... 注释内容... ' :<<! 注释内容... 注释内容... 注释内容... !
二、Shell变量
2.1 变量的命名
变量名=值
- 赋值“=”号两边不能有空格;
- 命名只能使用字母、数字、下划线且不能以数字开头;
- 不能使用标点符号;
- 不能使用bash中的关键字。(关键字可参考:http://blog.chinaunix.net/uid-25880122-id-2941630.html)
2.2 变量的使用
除了显式的标量赋值,还可以通过语句进行赋值,例如:
for file in `ls /etc` 或 for file in $(ls /etc)
循环遍历 /etc目录下的文件名。
ps:在shell中,"$" 和"`"(反引号,引用系统命令,包含的命令会被优先执行)的作用类似。
your_name="zhangsan" echo $your_name echo ${your_name}
使用"$"可以使用已经定义好的变量。花括号可用可不用,但为了代码的规范性,需要使用。即可以限定范围,代码也更具可读性。
myUrl="https://www.google.com" readonly myUrl
使用关键字 readonly 定义只读变量,则变量的值不能够改变。
myUrl="https://www.runoob.com" unset myUrl
使用关键字 unset 取消变量或函数的定义。
2.3 变量的类型
shell 中有四种类型的变量:用户自定义变量(局部变量)、环境变量、位置参数变量和预定义变量。
- 用户自定义变量:在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量;
- 环境变量:所有的程序,包括shell启动的程序,都能访问环境变量。当一个 shell 脚本程序开始执行时,一些变量会根据环境设置中的值进行初始化,这些变量通常用大写字母做名字,以便与用户自定义变量做区分,被称为环境变量。环境变量可以在当前 shell 和这个 shell 的所有子 shell 中生效。如果把环境变量写入相应的配置文件(如 /etc/profile ),那么这个环境变量就会在所有的 shell 中生效。系统自带的环境变量的名字不可更改,但是值可以按需更改。用户也可以使用 export 命令在 shell 中自己创建环境变量。
- 位置参数变量主要用来向脚本中传递参数或数据,变量名不能自定义,变量作用也是固定的。主要有以下几种位置参数变量:
- $1、$2、$3:脚本程序的参数,分别代表程序的第1个参数、第2个参数、... 程序第10个以上的参数需要用大括号包含,如 ${10};
- $*:代表命令行中的所有参数。在一个变量中将所有参数列出,各参数之间用环境变量 IFS 中的第一个字符分隔开;
- $@:和 $* 一样,也包含了命令行中的所有参数,使用时加引号,并在引号中返回每个参数,参数也是分开显示的;
- 区别:$* 将所有的参数视为一个整体,而 $@ 将所有的参数分别视为单独的个体。一般来说,采用 $@ 来访问脚本程序的参数会比较好,不必担心 IFS 所设置的分隔符为空而导致各参数连在一起分不清楚。
- 预定义变量是在 bash 中已经定义好了的变量,变量名不能自定义,变量作用也是固定的。实际上,位置参数变量就是预定义变量的一种。
- $?:保存最后一次执行的命令的返回状态。如果 $? 的值为 0 ,则表明上一个命令成功执行;如果值非 0 ,则表明上一个命令没有成功执行;
- $!:用于保存后运行的最后一个进程的 PID 号。
三、算数运算
shell 的算术运算符与 C 语言里的差不多,优先级与顺序也相同。但是,由于 shell 中所有变量都是被看做字符串来存储的,因此,要处理算术表达式,还需要使用一些特殊手段将数值型字符串转换成相应的数值。
3.1 使用expr命令对表达式进行求值
a=2 b=3 c=`expr $a + $b`
注意:在 expr 命令所支持的操作符中,“ | 、 & 、< 、<= 、> 、 >= 、 * ” 这几个需要用 \ 符进行转义再使用。此外,表达式的各字符之间需要用空格隔开。
a=5;b=6;c=0 echo $(expr $a \| $c) # 输出 5 echo $(expr $b \& $c) # 输出 0 echo $(expr $a \& $b) # 输出 5 echo $(expr $a \<= $b) # 输出 1 echo $(expr $a \* $b) # 输出 30 echo $(expr $a = 2) # 输出 1
3.2 使用$((...))的方式进行表达式求值(等价于 $[] )
expr 虽然功能强大,但是上面已经提到,在进行一些运算的时候,需要使用 \ 符来进行转义,这对于阅读代码的人来说并不友好。另一方面,expr 命令执行起来其实很慢,因为它需要调用一个新的 shell 来处理 expr 命令。更新更好的一种做法是使用 $((...)) 扩展的方式。只需要将准备求值的表达式放在 $((...)) 的括号中即可进行简单的算术求值。所有支持 $(( ... )) 的shell,都可以让用户在提供变量名称时,无须前置 $ 符。
a=5;b=6 echo $(($a + $b)) # 输出 11 。在变量名前加上 $,这在shell中一般是取变量值的意思 echo $((a + b)) # 输出 11 。可见,变量前不加 $ 也是可以的,为了简便,后面的代码就不加 $ 了 echo $((a | b)) # 输出 7 。这里的 | 是按位或操作符 echo $((a || b)) # 输出 1 。这里的 || 是逻辑或操作符 echo $((a & b)) # 输出 4 。这里的 & 是按位与操作符 echo $((a && b)) # 输出 1 。这里的 && 是逻辑与操作符 echo $((a * b)) # 输出 30 echo $((a == b)) # 输出 0
五、Shell字符串
your_name="runoob" # 使用双引号拼接 greeting="hello, "$your_name" !" greeting_1="hello, ${your_name} !" echo $greeting $greeting_1 # 使用单引号拼接 greeting_2='hello, '$your_name' !' greeting_3='hello, ${your_name} !' echo $greeting_2 $greeting_3 输出: hello, runoob ! hello, runoob ! hello, runoob ! hello, ${your_name} !
单引号:单引号中的任何字符都会原样输出,因此字符串中的变量是无效的;
双引号:双引号内可以出现变量也可以出现转义符。
str="abcd" echo ${str} # 输出 abcd # 使用 "#"输出字符串长度 echo ${#str} #输出 4 #提取子字符串 echo ${str:1:3} #输出 bcd
#ps:字符串的索引值从0开始
#查找字符的位置 string="runoob is a great site" echo `expr index "$string" io` # 输出 4 #ps:i 和o先查到那个就输出那个的位置,索引是从1开始
六、Shell数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
6.1 定义数组
在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
# 数组名=(值1 值2 ... 值n)
#例如:
array=(value0 value1 value2 value3)
还可以单独定义数组的各个分量:
array_name[0]=value0 array_name[1]=value1 array_name[n]=valuen
#ps:下标的范围没有限定
6.2 读取数组
#格式:
# ${数组名[下标]}
valuen=${array_name[n]}
使用 @ 符号可以获取数组中的所有元素,例如:
echo ${array_name[@]}
6.3 获取数组长度
# 取得数组元素的个数 length=${#array_name[@]} # 或者 length=${#array_name[*]} # 取得数组单个元素的长度 lengthn=${#array_name[n]}
七、Shell中的流程控制
7.1 test命令
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
num1=100 num2=100 if test $[num1] -eq $[num2] then echo '两个数相等!' else echo '两个数不相等!' fi
输出:两个数相等!
数值符号说明:
- -eq :等于则为真
- -ne :不等于则为真
- -gt :大于则为真
- -ge: 大于等于则为真
- -lt :小于则为真
- -le :小于等于则为真
字符串中运算符说明:
- = :等于则为真
- != :不相等则为真
- -z 字符串名:字符串的长度为零则为真
- -n 字符串名:字符串的长度不为零则为真
if test -e ./bash then echo '文件已存在!' else echo '文件不存在!' fi
文件运算中的符号说明:
- -e 文件名 :如果文件存在则为真
- -r 文件名 :如果文件存在且可读则为真
- -w 文件名 :如果文件存在且可写则为真
- -x 文件名 :如果文件存在且可执行则为真
- -s 文件名 :如果文件存在且至少有一个字符则为真
- -d 文件名 :如果文件存在且为目录则为真
- -f 文件名 :如果文件存在且为普通文件则为真
- -c 文件名 :如果文件存在且为字符型特殊文件则为真
- -b 文件名 :如果文件存在且为块特殊文件则为真
7.2 Shell中的if语句
格式:
if condition then command1 command2 ... commandN else command fi
if else-if else 语法格式:
if condition1 then command1 elif condition2 then command2 else commandN fi
示例:判断两个变量是否相等?
a=10 b=20 if [ $a == $b ] then echo "a 等于 b" elif [ $a -gt $b ] then echo "a 大于 b" elif [ $a -lt $b ] then echo "a 小于 b" else echo "没有符合的条件" fi
在实际应用中,if else 语句经常与 test 命令结合使用,如下所示:
num1=$[2*3] num2=$[1+5] if test $[num1] -eq $[num2] then echo '两个数字相等!' else echo '两个数字不相等!' fi
7.3 for循环
for循环一般格式为:
for var in item1 item2 ... itemN do command1 command2 ... commandN done #也可以写成一行 for var in item1 item2 ... itemN; do command1; command2… done;
ps:in 列表可以包含替换、字符串和文件名。
7.4 while语句
while 循环用于不断执行一系列命令,也用于从输入文件中读取数据。其语法格式为:
while condition do command done
示例代码:
#!/bin/bash int=1 while(( $int<=5 )) do echo $int let "int++" done
ps:以上实例使用了 Bash let 命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。
7.5 死循环
while : do command done #或者 while true do command done #或者 for (( ; ; ))
7.6 until循环
until 循环执行一系列命令直至条件为 true 时停止。
until 循环与 while 循环在处理方式上刚好相反。
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
until 语法格式:
until condition do command done
示例:
a=0 #直到a不小于10为止 until [ ! $a -lt 10 ] do echo $a a=`expr $a + 1` done
7.7 多选语句 case...esac
case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,是一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。
可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
case ... esac 语法格式如下:
case 值 in 模式1) command1 command2 ... commandN ;; 模式2) command1 command2 ... commandN ;; esac
case 工作方式如上所示,取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,可以使用星号 * 作为默认选项捕获该值(类似于default选项),再执行后面的命令。
下面的脚本提示输入 1 到 4,与每一种模式进行匹配:
echo '输入 1 到 4 之间的数字:' echo '你输入的数字为:' read aNum case $aNum in 1) echo '你选择了 1' ;; 2) echo '你选择了 2' ;; 3) echo '你选择了 3' ;; 4) echo '你选择了 4' ;; *) echo '你没有输入 1 到 4 之间的数字' ;; esac
ps:在Shell的循环中也可以使用break(跳出所有循环)和 continue(跳过本次循环)的语句。
八、Shell中的函数
格式:
[ function ] funname [()] { action; [return int;] }
说明;
- 可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
- 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。
demoFun(){ echo "这是我的第一个 shell 函数!" } echo "-----函数开始执行-----" demoFun echo "-----函数执行完毕-----"