Shell脚本编程_基础篇
Shell脚本编程
时间:2023/03
Shell变量的基本概念
Shell中的变量分为三种,分别是
环境变量(标准变量)、系统变量以及用户自定义变量(局部变量)
环境变量的值可被用户修改,而系统变量是由系统预定义的,用户只能引用但不能修改,另外用户可自定义变量,即用户自定义变量,可以对变量进行创建、赋值以及引用等。
环境变量
环境变量都被存放在/etc/profile文件中,如下通过cat命令查看/etc目录下的profile文件:
$ cat /etc/profile
环境变量有以下几种:
1.HOME变量:用于标识当前用户的主目录
2.PATH变量:搜索路径,该变量的值是以冒号:
隔开的多个目录名称,也就是一系列的目录名称,当运行一个命令(程序)时,就会从左到右地依次在这些目录中查找命令,找到了后就直接执行;若没有找到,则不会再去其他目录中查找。
目录名称 | 作用 |
---|---|
/bin | 存放二进制可执行文件,它存放着最经常使用的命令 |
/sbin | 通常也是存放二进制可执行文件,它存放的是系统管理员使用的系统管理程序 |
3.TERM变量:用于指定当前用户所用的终端类型。
在Linux终端中输入命令,可看到xterm-256color,即支持启用256色的Xterm:
4.SHELL变量:用于保存当前用户的初始Shell的路径名称,保存的路径是用户登录时所启动的Shell。
5.PS1变量:该变量用于保存系统中的基本提示符,默认值为“$”
$ echo $PS1
[\u@\h \W]\$
系统变量
Shell中的系统变量是系统定义并赋予初值的,用户只能引用但不能修改其值,常用的Shell系统变量如下表:
系统变量名称 | 作用 |
---|---|
$0 | 当前Shell脚本的名称,对应于命令行上输入的脚本名 |
$1,$2,……,$9 | 分别表示命令行上第1-9个命令行参数 |
$# | 命令行上的参数个数,不包括$0 |
$* | 命令行上的所有参数 |
$@ | 同上 |
$$ | 当前进程的进程号 |
$? | 上一条命令的退出状态 |
$! | 最后一个后台进程的进程号 |
$*
与$@
区别:
- 相同点:都是引用所有参数。
- 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则
*
等价于 "1 2 3"(传递了一个参数),而@
等价于 "1" "2" "3"(传递了三个参数)。
#!/bin/bash
echo "-- \$* 演示 ---"
for i in "$*"; do
echo $i
done
echo "-- \$@ 演示 ---"
for i in "$@"; do
echo $i
done
执行结果:
$ sh shell.sh 1 2 3
-- $* 演示 ---
1 2 3
-- $@ 演示 ---
1
2
3
当执行 shell 脚本时,$?
对应该脚本调用 exit 命令返回的值。如果没有主动调用 exit 命令,默认返回为 0。
另外一种说法:
来自菜鸟教程
变量类型
运行shell时,会同时存在三种变量:
1) 局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
2) 环境变量: 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
3) shell变量 :shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
查看变量(set 命令)
可以通过set 命令查看当前用户的环境变量,它也会显示用户自定义的变量。
$ A=111
$ set
A=111
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=()
BASH_SOURCE=()
只读变量(readonly 命令)
通过readonly 命令可以将一个变量变为只读变量,readonly后直接跟变量的名称,它的值不能被修改。
$ B=`pwd`
$ readonly B
$ B=`ls -l`
-bash: B: readonly variable
清除变量(unset 命令)
清除变量可以通过unset 命令,该命令后跟变量的名称(不用加上变量引导符$),当变量清除后其值为空。
$ A=`pwd`
$ echo $A
/home/sun
$ unset A
$ echo $A
Shell的引用符
一、双引号和单引号
在一个双引号中带有变量引导符$,则表示它会取这个变量的值,即双引号中可以有变量,同时也可以出现转义字符。
而单引号不一样,在单引号中其他字符的原有特殊含义都被屏蔽了,它们显示的是这些符号的基本含义
$ number=12345
$ A="He is $number"
$ B='He is $number'
$ echo $A
He is 12345
$ echo $B
He is $number
二、反引号
反引号用于将反引号其中的字符串当作命令来执行,即该命令执行的结果被直接保存在变量中。
$ A=`pwd`
$ echo "current work pwd is $A"
current work pwd is /home/sun
三、花括号
花括号可以使变量与其后跟的其他字符隔开
$ echo $Aer
$ echo ${A}er
/home/suner
字符串操作
查找子字符串、提取子字符串、获取字符串长度、拼接字符串
$ number="this is the test"
$ echo `expr index "$number" i`
3
$ echo `expr index "$number" e`
11
$ echo ${#number}
16
$ echo ${#number[0]} #${#number} 等价于 ${#number[0]}:
16
$ echo ${number:1:2}
hi
$ combine="$B,$number"
$ echo $number $combine
this is the test /home/sun,this is the test
expr命令解释:
expr命令来自于英文单词expression的缩写,中文译为表达式,其功能是用于字符串及整数计算。Linux系统求表达式变量值的工具,亦可用于计算整数值或字符串的结果,当作计算器也没问题的。
语法格式:expr [表达式]
常用参数:
参数 | 说明 |
---|---|
length | 统计字符串长度 |
substr | 指定截取字符位置 |
index | 找到某个字符最先出现的位置 |
$ expr length "This is a test"
14
$ expr substr "This is a test" 1 4 #从第1字符个开始的4个字符
This
$ expr index "This is a test" i
3
$ expr 5+5
5+5
$ expr 5 + 5
10
$ expr "\( 10 + 10 \) \* 2 + 100" #加了""后,解释成字符串,无法计算结果
\( 10 + 10 \) \* 2 + 100 #进行一系列的简单整数运算,其中乘法(*)需转义符
$ expr \( 10 + 10 \) \* 2 + 100
140
Shell数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
定义数组
在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。
数组名=(值1 值2 ... 值n)
$ array_name=(value0 value1 value2 value3)
或者
array_name=(
value0
value1
value2
value3
)
读取数组
${数组名[下标]}
使用@
或*
符号可以获取数组中的所有元素
echo ${array_name[@]}
获取数组的长度
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}
举例说明:
$ array_name=(value0 value1 value2 value3)
$ echo ${array_name[1]}
value1
$ echo ${array_name[@]}
value0 value1 value2 value3
$ echo ${#array_name[@]}
4
$ echo ${#array_name[*]}
4
$ echo ${array_name[*]}
value0 value1 value2 value3
$ echo ${#array_name[1]}
6
$ echo ${#array_name[0]}
6
Shell关联数组(字典key-value)
Bash 支持关联数组,可以使用任意的字符串、或者整数作为下标来访问数组元素。
关联数组使用 declare 命令来声明,语法格式如下:
declare -A array_name
-A
选项就是用于声明一个关联数组。
关联数组的键是唯一的。
实例:
declare -A site=(["google"]="www.google.com" ["runoob"]="www.runoob.com" ["taobao"]="www.taobao.com")
也可以先声明一个关联数组,然后再设置键和值:
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"
举例说明:
$ cat shell.sh
#!/bin/bash
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"
echo "访问关联数组的元素:${site["google"]}"
echo "获取数组中的所有元素:${site[*]}"
echo "获取数组中的所有元素:${site[@]}"
echo "获取数组的所有键:${!site[*]}"
echo "获取数组的所有键:${!site[@]}"
echo "获取数组的个数:${#site[*]}"
执行结果
$ sh shell.sh
访问关联数组的元素:www.google.com
获取数组中的所有元素:www.google.com www.runoob.com www.taobao.com
获取数组中的所有元素:www.google.com www.runoob.com www.taobao.com
获取数组的所有键:google runoob taobao
获取数组的所有键:google runoob taobao
获取数组的个数:3
Shell 注释
以 #
开头的行就是注释,会被解释器忽略。
通过每一行加一个 #
号设置多行注释
#--------------------------------------------
# 这是一个注释
# author:教程
# site:www.baidu.com
# slogan:学的不仅是技术,更是梦想!
#--------------------------------------------
##### 用户配置区 开始 #####
多行注释
格式:
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
实例:
:<<'
注释内容...
注释内容...
注释内容...
'
或
:<<!
注释内容...
注释内容...
注释内容...
!
Shell传递参数
在执行Shell脚本时,向脚本传递参数,脚本内获取参数的格式为:$n
。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…… $0
为执行的文件名
#!/bin/bash
# author:sun
echo "Shell 传递参数实例!"
echo "执行的文件名:$0"
echo "第一个参数为:$1"
echo "第二个参数为:$2"
echo "第三个参数为:$3"
几个特殊字符用来处理参数:
#!/bin/bash
# author:sun
echo "Shell 传递参数实例!"
echo "第一个参数为:$1"
echo "参数个数为:$#"
echo "1传递的参数作为一个字符串显示:$*"
echo "2传递的参数作为多个字符串显示:$@"
执行结果:
$ sh shell.sh 1 2 3
Shell 传递参数实例!
第一个参数为:1
参数个数为:3
1传递的参数作为一个字符串显示:1 2 3
2传递的参数作为一个字符串显示:1 2 3
Shell基本运算符
Shell和其他编程语言一样,支持多种运算符,包括:
- 算数运算符
- 关系运算符
- 布尔运算符
- 字符串运算符
- 文件测试运算符
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
例如,两个数相加(注意使用的是反引号 ` 而不是单引号 '):
#!/bin/bash
val=`expr 2 + 2`
echo "两数之和为 : $val"
算术运算符
下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20:
运算符 | 说明 | 举例 |
---|---|---|
+ | 加法 | expr $a + $b 结果为 30。 |
- | 减法 | expr $a - $b 结果为 -10。 |
* | 乘法 | expr $a \* $b 结果为 200。 |
/ | 除法 | expr $b / $a 结果为 2。 |
% | 取余 | expr $b % $a 结果为 0。 |
= | 赋值 | a=$b 把变量 b 的值赋给 a。 |
== | 相等。用于比较两个数字,相同则返回 true。 | [ $a == $b ] 返回 false。 |
!= | 不相等。用于比较两个数字,不相同则返回 true。 | [ $a != $b ] 返回 true。 |
关系运算符
运算符 | 说明 | 举例 |
---|---|---|
-eq | 检测两个数是否相等,相等返回 true。 | [ $a -eq $b ] 返回 false。 |
-ne | 检测两个数是否不相等,不相等返回 true。 | [ $a -ne $b ] 返回 true。 |
-gt | 检测左边的数是否大于右边的,如果是,则返回 true。 | [ $a -gt $b ] 返回 false。 |
-lt | 检测左边的数是否小于右边的,如果是,则返回 true。 | [ $a -lt $b ] 返回 true。 |
-ge | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ $a -ge $b ] 返回 false。 |
-le | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ $a -le $b ] 返回 true。 |
布尔运算符
运算符 | 说明 | 举例 |
---|---|---|
! 非运算 | 表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 |
-o或运算 | 有一个表达式为 true 则返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a与运算 | 两个表达式都为 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
逻辑运算符
运算符 | 说明 | 举例 |
---|---|---|
&& |
逻辑的AND | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
两个竖线 | 逻辑的OR | [[ $a -lt 100 两个竖线 $b -gt 100 ]] 返回 true |
|| 因为在markdown中显示为空,无法正常显示,用两个竖线表示表示
字符串运算符
运算符 | 说明 | 举例 |
---|---|---|
= | 检测两个字符串是否相等,相等返回 true。 | [ $a = $b ] 或者[[ $a == $b ]] 返回 false。 |
!= | 检测两个字符串是否不相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 检测字符串长度是否为0,为0返回 true。 | [ -z $a ] 返回 false。 |
-n | 检测字符串长度是否不为 0,不为 0 返回 true。 | [ -n "$a" ] 返回 true。 |
$ | 检测字符串是否不为空,不为空返回 true。 | [ $a ] 返回 true。 |
实例:
#!/bin/bash
a="abc"
b="efg"
if [ $a = $b ]
then
echo "$a = $b : a 等于 b"
else
echo "$a = $b: a 不等于 b"
fi
if [ $a != $b ]
then
echo "$a != $b : a 不等于 b"
else
echo "$a != $b: a 等于 b"
fi
if [ -z $a ]
then
echo "-z $a : 字符串长度为 0"
else
echo "-z $a : 字符串长度不为 0"
fi
if [ -n "$a" ]
then
echo "-n $a : 字符串长度不为 0"
else
echo "-n $a : 字符串长度为 0"
fi
if [ $a ]
then
echo "$a : 字符串不为空"
else
echo "$a : 字符串为空"
fi
[条件]
和[[条件]]
的区别:
在shell脚本中,[ ]
和 [[ ]]
是用于条件测试的不同运算符,它们之间有一些重要的区别:
[ ]
是旧的测试运算符,而[[ ]]
是新的测试运算符。[[ ]]
支持更多的条件测试选项,例如字符串匹配和正则表达式匹配,而[ ]
不支持这些高级条件测试。[[ ]]
在条件测试时更加强大和灵活。它不需要引号来包围变量,可以处理包含空格的变量值,还支持逻辑运算符(如&&
和||
)。[[ ]]
更安全,不会出现因为特殊字符或空格而导致的意外行为。
下面是一些示例,说明了[ ]
和 [[ ]]
之间的差异:
使用[ ]
进行条件测试:
string="hello world"
if [ "$string" == "hello" ]; then
echo "条件测试成功"
else
echo "条件测试失败"
fi
使用[[ ]]
进行条件测试:
string="hello world"
if [[ $string == "hello" ]]; then
echo "条件测试成功"
else
echo "条件测试失败"
fi
总的来说,建议在新的shell脚本中使用[[ ]]
,因为它更强大和灵活,而且更不容易出现问题。但是,在旧的脚本中,可能会看到使用[ ]
的情况。
文件测试运算符
文件测试运算符用于检测 Unix 文件的各种属性。
属性检测描述如下:
操作符 | 说明 | 举例 |
---|---|---|
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回 false。 |
-d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回 true。 |
-g[1] file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回 false。 |
-k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] 返回 false。 |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] 返回 false。 |
-u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] 返回 false。 |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回 true。 |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |
其他检查符:
-S: 判断某文件是否 socket。
-L: 检测文件是否存在并且是一个符号链接。
实例:
#!/bin/bash
file="/var/www/runoob/test.sh"
if [ -r $file ]
then
echo "文件可读"
else
echo "文件不可读"
fi
if [ -w $file ]
then
echo "文件可写"
else
echo "文件不可写"
fi
if [ -x $file ]
then
echo "文件可执行"
else
echo "文件不可执行"
fi
if [ -f $file ]
then
echo "文件为普通文件"
else
echo "文件为特殊文件"
fi
if [ -d $file ]
then
echo "文件是个目录"
else
echo "文件不是个目录"
fi
if [ -s $file ]
then
echo "文件不为空"
else
echo "文件为空"
fi
if [ -e $file ]
then
echo "文件存在"
else
echo "文件不存在"
fi
Shell echo命令
1.显示普通字符串:
echo "It is a test"
或者
echo It is a test
2.显示转义字符
echo "\"It is a test\""
结果是:
"It is a test"
3.显示变量
read
命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量
#!/bin/bash
echo "Input your userid:"
read userid
echo "Your userid is $userid"
echo "Are your sure:y/n"
read answer
if [ $answer == "y" -o $answer == "Y" ]
then
useradd $userid
else
"Your input is not correct!!"
fi
userid 接收标准输入的变量,结果将是:
[root@www ~]# sh test.sh
zi1024 #标准输入
Your userid is zi1024 #输出
...
4.显示换行
echo -e "OK! \n" # -e 开启转义
echo "It is a test"
输出结果:
OK!
It is a test
5.显示不换行
#!/bin/sh
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"
输出结果:
OK! It is a test
6.显示结果定向至文件
echo "It is a test" > myfile
7.原样输出字符串,不进行转义或取变量(用单引号)
echo '$name\"'
输出结果:
$name\"
8.显示命令执行结果
echo `date`
Mon Mar 13 04:34:48 EDT 2023
Shell printf 命令
printf 命令用于格式化输出,类似于C/C++中的printf函数。
printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。
默认的 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n
printf 命令的语法:
printf format-string [arguments...]
- format-string: 为格式控制字符串
- arguments: 为参数列表。
# cat echo3.sh
#!/bin/bash
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
结果:
# sh echo3.sh
姓名 性别 体重kg
郭靖 男 66.12
杨过 男 48.65
郭芙 女 47.99
%d %s %c %f
格式替代符详解:
替代符 | 说明 |
---|---|
d: Decimal | 十进制整数 对应位置参数必须是十进制整数,否则报错! |
s: String | 字符串 对应位置参数必须是字符串或者字符型,否则报错! |
c: Char | 字符 对应位置参数必须是字符串或者字符型,否则报错! |
f: Float | 浮点 对应位置参数必须是数字型,否则报错! |
%o | 八进制整数 |
%x | 十六进制整数 |
# printf "%d %s %c\n" 1 "abc" "def"
1 abc d
%-10s
指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f
指格式化为小数,其中 .2
指保留2位小数。
printf "%10d.\n" 123 #占10位,右对齐
printf "%-10.2f.\n" 123.123321 #占10位,保留2位小数,左对齐
printf "My name is %s\n" "Arthur" # 格式化输出字符串
printf "%d" * "%d" = "%d\n" 2 3 `epxr 2 \* 3` #表达式的值作为参数
printf 的转义序列
序列 | 说明 |
---|---|
\a | 警告字符 |
\b | 后退 |
\c | 抑制输出结果中任何结尾的换行字符,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ | 一个字面上的反斜杠字符 |
\ddd | 表示1到3位数八进制值的字符,仅在格式字符串中有效 |
\0ddd | 表示1到3位的八进制值字符 |
$ printf "a string, no processing:<%s>\n" "A\nB"
a string, no processing:<A\nB>
$ printf "a string, no processing:<%b>\n" "A\nB"
a string, no processing:<A
B>
$ printf "www.runoob.com \a"
www.runoob.com $
以下是对每个示例的解释:
-
$ printf "a string, no processing:<%s>\n" "A\nB"
- 这里使用了
%s
格式说明符,它不会处理字符串中的特殊字符。 - 结果是字符串 "A\nB" 被原样打印出来,不会将
\n
解释为换行符。
- 这里使用了
-
$ printf "a string, no processing:<%b>\n" "A\nB"
- 这里使用了
%b
格式说明符,它会处理字符串中的特殊字符。 - 结果是字符串 "A\nB" 被处理为换行符,所以 "A" 和 "B" 分别位于两行。
- 这里使用了
-
$ printf "www.runoob.com \a"
- 这里使用了
\a
,这是一个控制字符,代表响铃(发出声音或闪烁光标)。 - 结果是 "www.runoob.com" 被打印,然后光标回到行首,因此
$
符号出现在字符串的末尾。
- 这里使用了
Shell test 命令
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
数值测试
参数 | 说明 |
---|---|
-eq | 等于则为真 |
-ne | 不等于则为真 |
-gt | 大于则为真 |
-ge | 大于等于则为真 |
-lt | 小于则为真 |
-le | 小于等于则为真 |
#!/bin/bash
num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi
代码中的 [ ]
执行基本的算数运算,如:
#!/bin/bash
a=5
b=6
result=$[a+b] # 注意等号两边不能有空格
echo "result 为: $result"
字符串测试
参数 | 说明 |
---|---|
= | 等于则为真 |
!= | 不相等则为真 |
-z 字符串 | 字符串的长度为零则为真 |
-n 字符串 | 字符串的长度不为零则为真 |
num1="ru1noob"
num2="runoob"
if test $num1 = $num2
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi
文件测试
参数 | 说明 |
---|---|
-e 文件名 | 如果文件存在则为真 |
-r 文件名 | 如果文件存在且可读则为真 |
-w 文件名 | 如果文件存在且可写则为真 |
-x 文件名 | 如果文件存在且可执行则为真 |
-s 文件名 | 如果文件存在且至少有一个字符则为真 |
-d 文件名 | 如果文件存在且为目录则为真 |
-f 文件名 | 如果文件存在且为普通文件则为真 |
-c 文件名 | 如果文件存在且为字符型特殊文件则为真 |
-b 文件名 | 如果文件存在且为块特殊文件则为真 |
cd /bin
if test -e ./bash
then
echo '文件已存在!'
else
echo '文件不存在!'
fi
另外,Shell 还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为: !
最高, -a
次之, -o
最低。
cd /bin
if test -e ./notFile -o -e ./bash
then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi
Shell 流程控制
在 sh/bash 里如果 else 分支没有语句执行,就不要写这个 else。
if
if 语句语法格式:
if condition
then
command1
command2
...
commandN
fi
if else
if else 语法格式:
if condition
then
command1
command2
...
commandN
else
commandh
fi
if else-if else
if else-if else 语法格式:
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
if else 的 [...]
判断语句中大于使用 -gt,小于使用 -lt。
if [ "$a" -gt "$b" ]; then
...
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 (( a > b )); then
...
fi
a=10
b=20
if (( $a == $b ))
then
echo "a 等于 b"
elif (( $a > $b ))
then
echo "a 大于 b"
elif (( $a < $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
for 循环
for循环一般格式为:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
写成一行:
for var in item1 item2 ... itemN; do command1; command2… done;
例子
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
输出结果:
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
while 语句
while 循环用于不断执行一系列命令,也用于从输入文件中读取数据。
其语法格式为:
while condition
do
command
done
例子
#!/bin/bash
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done
Bash let命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量.
如果表达式中包含了空格或其他特殊字符,则必须引起来。
自加操作:let no++
自减操作:let no--
简写形式 let no+=10,let no-=20,分别等同于 let no=no+10,let no=no-20。
以下实例计算 a 和 b 两个表达式,并输出结果:
#!/bin/bash
let a=5+4
let b=9-3
echo $a $b
读入输入值的while循环:
echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
echo "是的!$FILM 是一个好网站"
done
运行脚本,输出类似下面:
按下 <CTRL-D> 退出
输入你最喜欢的网站名:shell
是的!shell 是一个好网站
无限循环
无限循环语法格式:
while :
do
command
done
或者
while true
do
command
done
until 循环
until 循环执行一系列命令直至条件为 true 时停止。
until 循环与 while 循环在处理方式上刚好相反。
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
until 语法格式:
until condition
do
command
done
condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
以下实例我们使用 until 命令来输出 0 ~ 9 的数字:
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done
运行结果:
0
1
2
3
4
5
6
7
8
9
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
,每一模式必须以右括号
结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;
。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号*
捕获该值,再执行后面的命令。
下面的脚本提示输入 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
输入不同的内容,会有不同的结果,例如:
输入 1 到 4 之间的数字:
你输入的数字为:
3
你选择了 3
下面的脚本匹配字符串:
#!/bin/sh
site="baidu"
case "$site" in
"baidu") echo "百度"
;;
"google") echo "Google 搜索"
;;
"taobao") echo "淘宝网"
;;
esac
跳出循环
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break 和 continue。
break 命令
break 命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done
执行以上代码,输出结果为:
输入 1 到 5 之间的数字:3
你输入的数字为 3!
输入 1 到 5 之间的数字:7
你输入的数字不是 1 到 5 之间的! 游戏结束
continue
continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
对上面的例子进行修改:
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字: "
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的!"
echo "游戏结束"
break
;;
esac
done
运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句 echo "游戏结束" 永远不会被执行。
Shell 函数
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
shell中函数的定义格式如下:
[ function ] funname [()]
{
action;
[return int;]
}
- 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
- 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)
$ cat demoFun.sh
#!/bin/bash
demoFun(){
echo "飞流直下三千尺,"
echo "疑是银河落九天。"
}
echo "====start===="
demoFun
echo "====end======"
输出结果:
$ sh demoFun.sh
====start====
飞流直下三千尺,
疑是银河落九天。
====end======
定义一个带有return语句的函数:
#!/bin/bash
funReturn(){
echo "开启加法计算器:"
echo -n "输入第一个数字:"
read num1
echo -n "输入第二个数字:"
read num2
echo "输入的数字分别为:$num1和$num2"
return $(( $num1 + $num2 ))
}
funReturn
echo "两个数值之和是:$?"
echo "计算结束!"
计算结果:
$ sh funReturn.sh
开启加法计算器:
输入第一个数字:2
输入第二个数字:5
输入的数字分别为:2和5
两个数值之和是:7
计算结束!
函数参数
#!/bin/bash
funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
输出结果:
第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11 个!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
注意,$10
不能获取第十个参数,获取第十个参数需要${10}
。当n>=10时,需要使用${n}来获取参数。
参数处理 | 说明 |
---|---|
$# | 传递到脚本或函数的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
$ echo $-
himBH
$-
记录着当前设置的shell选项,himBH是默认值,5个字母分别有各自含义。解释如下:
- h:hashall,打开这个选项后,Shell 会将命令所在的路径记录下来,避免每次都要查询。举例:当h选项开启时,如果将某个自定义命令从/usr/bin/目录下移动到/usr/local/bin/,再运行,会提示无此命令。而当通过set +h将h选项关闭后,上述情况就不会出现。
- i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的 shell。所谓的交互式shell,就是输入命令后,shell解释执行,然后返回一个结果。在脚本中,i选项是关闭的。
- m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。
- B:braceexpand,大括号扩展。
- H:history,Shell 会把我们执行的命令记录下来,可以通过 history 命令查看,每一行是序号 + 执行的命令,在 shell 退出时,会将这些信息保存到~/.bash_history 文件中。如果H选项打开,就可以展开历史列表中的命令,可以通过!感叹号来完成,例如"!!"返回上最近的一个历史命令,"!n"返回第 n 个历史命令,等等。
Shell 输入/输出重定向
重定向命令列表如下:
命令 | 说明 |
---|---|
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 之间的内容作为输入。 |
※需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
默认情况下,command > file
将 stdout 重定向到 file,command < file
将stdin 重定向到 file。
$ wc -l /etc/passwd > 1.txt
$ cat 1.txt
20 /etc/passwd
$ wc -l < /etc/passwd > 1.txt
$ cat 1.txt
20
执行wc -l,从文件/etc/passwd读取内容,然后将输出写入到1.txt中。
如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:
$ command > file 2>&1
或者
$ command >> file 2>&1
/dev/null 文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
$ command > /dev/null
**/dev/null **是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写:
$ command > /dev/null 2>&1
注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出。
Shell 文件包含
和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
Shell 文件包含的语法格式如下:
**. **filename # 注意点号(.)和文件名中间有一空格
或
source filename
$ cat 1.sh && cat 2.sh
#!/bin/bash
echo "===test环境密码更改==="
userpasswd="123456"
#!/bin/bash
echo -n "输入你的密码:"
read num1
source ./1.sh
if [ $num1 == $userpasswd ];then
echo "密码正确"
else
echo "密码错误!"
fi
$ sh 2.sh
输入你的密码:123456
===test环境密码更改===
密码正确
$ sh 2.sh
输入你的密码:1
===test环境密码更改===
密码错误!
注:被包含的文件 1.sh 不需要可执行权限。
综合使用
1.逐行读取文件
#!/bin/bash
while read line
do
echo "Line contents are : $line "
done < mycontent.txt
实际应用
需求:提取所有用户的密码策略
#!/bin/bash
echo "=====开始获取用户密码策略====="
awk -F : '{print $1}' /etc/passwd > /root/tmp.txt
count=1
while read line
do
echo "=====No.${count}:用户名:${line}====" >> /root/passwd.txt
chage -l $line >> /root/passwd.txt
let count++
done < /root/tmp.txt
echo 用户数总计:`cat /etc/passwd | wc -l`
echo "=========执行结束============="
14.其他用法补充
set -o
在shell中,set -o命令用于打开或关闭shell的某些选项。这些选项可以影响shell的行为和操作方式。
下面是一些常用的set -o选项:
set -o errexit
或 set -e
:使脚本在执行过程中遇到任何非零退出状态的命令时立即退出。
set -o nounset
或 set -u
:在使用未定义变量时引发错误,并使脚本退出。
set -o pipefail
:使整个管道的退出状态是管道中最后一个命令的退出状态。
set -o noclobber
:防止使用>符号覆盖或重写现有文件。
set -o xtrace
或 set -x
:打印脚本中每个执行的命令和其参数,以便于调试和跟踪脚本的执行流程。
要打开或关闭这些选项,可以使用set -o命令,并将选项名称作为参数,后面加上on或off。例如,要打开errexit选项,可以使用以下命令:
set -o errexit 或者 set -e
要关闭该选项,可以使用以下命令:
set +o errexit 或者 set +e
要查看当前设置的选项,可以使用set -o命令,不带任何参数。例如,要查看所有选项的当前状态,可以使用以下命令:
# set -o
allexport off
braceexpand on
emacs on
errexit off
errtrace off
functrace off
hashall on
histexpand on
history on
ignoreeof off
interactive-comments on
keyword off
monitor on
noclobber off
noexec off
noglob off
nolog off
notify off
nounset off
onecmd off
physical off
pipefail off
posix off
privileged off
verbose off
vi off
xtrace off