#职业能力目标和要求
1,理解shell script。
2,掌握判断式的用法。
3,掌握条件判断式的用法。
4,掌握循环的用法。
**1,功能:**能提供数组,循环,条件与逻辑判断等功能。
2,shell script可以被看出批处理文件,也可被看成程序语言。
3,支持排错(debug),可以更好的管理主机。
1,书写注意
2,运行shell script程序
直接命令下达:运行shell.sh,该文件必须具备rx权限
以bash程序来运行,通过“bash shell.sh"或“sh shell.sh"来运行。
为什么sh shell.sh可以运行呐?
3,编写第一个shell script程序
#查看Linux环境变量
[root@localhost scripts]# env
[root@localhost ~]# mkdir scripts
[root@localhost ~]# cd scripts/
[root@localhost scripts]# vim sh01.sh
#!/bin/bash #加载环境配置文件/声明shell
# Program: #井号表示注释
# This program shows "Hello World!" in your screen.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
echo -e "Hello World! \a \n" #主程序
exit 0 #运行结果告知(定义回传值)
#将script放置到家目录~/scripts目录中
#.sh有文件权限
[root@localhost scripts]# ll
total 4
-rw-r--r--. 1 root root 240 Nov 27 09:57 sh01.sh
script的文件头包含
1,对话式脚本:变量内容由使用者决定
#要求:使用read命令撰写个scripto让用户输人frstnamne与last name后,在屏幕上显示“Your full name is:”的内容
[root@localhost scripts]# vim sh02.sh
#!/bin/bash
# Program:
#User inputs hisfirst name andlast name. Program shows his full name.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
read -p "Please input your first name:" firstname
read -p "Please input your last name:" lastname
echo -e "\nYour full name is:$firstname $lastname"
[root@localhost scripts]# sh sh02.sh
Please input your first name:1
Please input your last name:1
Your full name is:1 1
2,随日期变化:利用date进行文件的创建
#创建三个空文件验证
[root@localhost scripts]# vim sh03.sh
#!/bin/bash
# Program:
#Program creates three files,which named by user's input and date command.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
#让使用者输入文件名称,并获得fileuser这个变量
echo -e "I wile use 'touch' command to create 3 files."
read -p "Please input your filename:" fileuser
#为了避免用户随意按“Enter”键,利用变量功能分析文件名是否设置?
filename=${fileuser:-"filename"}
#取得所需的文件名
date1=${date --date='2 days ago' +%Y%m%d}
date1=${date --date='1 days ago' +%Y%m%d}
date3=${date +%Y%m%d}
#这三行设置文件名
file1=${filename}${date1}
file2=${filename}${date2}
file3=${filename}${date3}
#创建文件
touch "$file1"
touch "$file2"
touch "$file3"
#运行结果
[root@localhost scripts]# sh sh03.sh
I wile use 'touch' command to create 3 files.
Please input your filename:script
[root@localhost scripts]# ll
total 12
-rw-r--r--. 1 root root 0 Nov 27 14:51 script20231125
-rw-r--r--. 1 root root 0 Nov 27 14:51 script20231126
-rw-r--r--. 1 root root 0 Nov 27 14:51 script20231127
-rw-r--r--. 1 root root 233 Nov 27 11:43 sh01.sh
-rw-r--r--. 1 root root 365 Nov 27 11:44 sh02.sh
-rw-r--r--. 1 root root 876 Nov 27 14:50 sh03.sh
3,数值运算:简单的加减乘除
#declare来定义变量的类型,利用“$((计算式))”来进行数值运算。bash shell默认是整数。
#输入两个变量,将两个变量内容相乘,最后输出相乘的结果。
[root@localhost scripts]# cat sh04.sh
#!/bin/bash
# Program:
#User input 2 integer numbers;program will cross three two numbers.
# History
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
echo -e "You SHOULD input 2 numbers, I will cross them!\n"
read -p "first number:" firstnu
read -p "second number:" secnu
total=$(($firstnu*$secnu))
echo -e "\nThe result of $firstnu $secnu is ==>$total"
#结果
[root@localhost scripts]# sh sh04.sh
You SHOULD input 2 numbers, I will cross them!
first number:2
second number:3
The result of 2 3 is ==>6
在数值的运算上,可以使用“declare i total-SfirstnsSsecnu",也可以使用上面的方式来表示。建议使用下面的方式进行运算:var=$( (运算内容))
可以利用source或者小数点(.)来运行成序。
1,利用直接运行的方式来运行脚本
#如果直接运行该命令时,该命令配置的firename会不会生效?
[root@localhost scripts]# echo $firstname $lastname #首先确定变量并不存在
[root@localhost scripts]# sh sh02.sh
Please input your first name:Bobby
Please input your last name:Yang
Your full name is:Bobby Yang #在脚本运行中,这两个变量会生效
[root@localhost scripts]# echo $firstname $lastname
<==这两个变量在父程序的bash中还是不存在
当你使用直接运行的方法来处理时,系统会开辟一个新的bash来运行sh02.sh里边的内容。
2,利用source运行脚本:在父程序中运行
[root@localhost scripts]# source sh02.sh
Please input your first name:Bobby
Please input your last name:Yang
Your full name is:Bobby Yang #在script运行中,这两个变量会生效
[root@localhost scripts]# echo $firstname $lastname
Bobby Yang #有数据产生
#检查/dmtsai是否存在时
[root@localhost ~]# test -e /dmtsai
#运行结果并不会显示任何信息,但最后可以通过$?或&&或||来显示整个结果
[root@localhost ~]# test -e /dmtsai && echo "exist0" || echo "Not exist"
Not exist
[root@localhost scripts]# vim sh05.sh
#!/bin/bash
# Program:
# User input a filename, program will check the flowing:
# 1.)exist? 2.)file/directory? 3.)file permissions
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
#让使用者输入文件名,并且判断使用者是否输人了字符串
echo -e "Please input a filename, I will check the filename's type and permission \n\n"
read -p "Input a filename : " filename
test -z $filename && echo "You MUST input a filename." && exit 0
#判断文件是否存在,若不存在则显示信息并结束脚本
test ! -e $filename && echo "The filename '$filename' DO NOT exist" && exit 0
#开始判断文件类型与属性
test -f $filename && filetype="regulare file"
test -d $filename && filetype="directory"
test -r $filename && perm="readanle"
test -w $filename && perm="$perm writable"
test -x $filename && perm="$perm executable"
#开始输出信息
echo "The filename: $filename is a $filetype"
echo "And the permissions are : $perm"
#结果
[root@localhost scripts]# sh sh05.sh
Please input a filename, I will check the filename's type and permission
Input a filename : sh02.sh
The filename: sh02.sh is a regulare file
And the permissions are : readanle writable
#判断$HOME变量是否为空
[root@localhost ~]# [ -z "$HOME0" ] #[]两端必须为空格字符分隔。
[root@localhost ~]# echo $?
0
#-z string含义是,若string长度为0,则为真。
[root@localhost scripts]# vim sh06.sh
#!/bin/bash
# Program:
# This program shows the's choice
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
read -p "Please input (Y/N): " yn
#“=”与"=="是一样的效果
#[ ]中的逻辑与和逻辑或使用-a 和-o 表示(-a: and, -o: or)
[ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue" && exit 0
[ "$yn" == "N" -o "$yn" == "n" ] && echo "Oh, interrupt" && exit 0
echo "I don't know what your choice is" && exit 0
#结果
[root@localhost scripts]# sh sh06.sh
Please input (Y/N): n
Oh, interrupt
1,单层,简单条件判断式
if [条件判断式];then
当条件判断式成立时,可以进行的命令工作内容;
fi <==将if反写,结束if之意
#将sh06.sh修改成if...then形式
[root@localhost scripts]# cp sh06.sh sh06-2.sh
[root@localhost scripts]# vim sh06-2.sh
#!/bin/bash
# Program:
# This program shows the's choice
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
read -p "Please input (Y/N): " yn
#“=”与"=="是一样的效果
if [ "$yn" == "Y" ] || [ "$yn" == "y" ] ;then
echo "OK, continue"
exit 0
fi
if [ "$yn" == "N" ] ||[ "$yn" == "n" ] ;then
echo "Oh, interrupt"
exit 0
fi
echo "I don't know what your choice is" && exit 0
[root@localhost scripts]# sh sh06-2.sh
Please input (Y/N): y
OK, continue
2,多重,复杂条件判断式
#一个条件判断,分成功进行与失败进行(else)
if [条件判断式]; then
当条件判断式成立时,可以进行的命令工作内容;
else
当条件判断式不成立时,可以进行的命令工作内容;
fi
#多个条件判断(if...elif...elif... else)分多种不同情况运
if [条件判断式一]; then
当条件判断式成立时, 可以进行的命令工作内容:
elif [ 条件判断式二]; then
当条件判断式二成立时,可以进行的命令工作内容;
else
当条件判断式一与二均不成立时,可以进行的命令工作内容;
fi
[root@localhost scripts]# vim sh06-3.sh
#!/bin/bash
# Program:
# This program shows the's choice
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
read -p "Please input (Y/N): " yn
#“=”与"=="是一样的效果
if [ "$yn" == "Y" ] || [ "$yn" == "y" ] ;then
echo "OK, continue"
elif [ "$yn" == "N" ] ||[ "$yn" == "n" ] ;then
echo "Oh, interrupt"
else
echo "I don't know what your choice is" && exit 0
fi
[root@localhost scripts]# sh sh06-3.sh
Please input (Y/N): n
Oh, interrupt
[root@localhost scripts]# vim sh09.sh
#!/bin/bash
# Program:
# Check $1 is wqual to "hello"
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
if [ "$1" == "hello" ]; then
echo "Hello, how are you ?"
elif [ "$1" == " " ]; then
echo "You MUST input parmeters, ex> {$0 someword}"
else
echo "The only parameter is 'hello', ex> {$0 hello}"
fi
[root@localhost scripts]# sh sh09.sh
**3,netstat命令:**查询到目前主机开启的网络服务端口(service posts)。
[root@localhost ~]# netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN
#封包格式 本地IP:端口 远程ip:端口 是否监听
#重点是“Local Address(本地主机的IP与端口对列)”,代表的是本机所启动的网络服务。
4,常见port与相关网络服务
[root@localhost scripts]# vim sh10.sh
#!/bin/bash
# Program:
# Using netstat and grep to detect WWW.SSH,FTP and Mail services.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
#先做一些告诉的动作
echo "Now, I will detect your ,Linux server's services!"
echo -e "The www, ftp, ssh, and mail will be detect! \n"
#开始进行一些测试的工作,并且也输出一些信息
testing=$(netstat--tuln | grep ":80 ")
if [ "$testing" != "" ]; then
echo "WWW is running in your system."
fi
testing=$(netstat--tuln | grep ":22 ")
if [ "$testing" != "" ]; then
echo "WWW is running in your system."
fi
testing=$(netstat--tuln | grep ":21 ")
if [ "$testing" != "" ]; then
echo "WWW is running in your system."
fi
testing=$(netstat--tuln | grep ":25 ")
if [ "$testing" != "" ]; then
echo "WWW is running in your system."
fi
[root@localhost scripts]# sh sh10.sh
#本处是3/6缩进
case $变量名称in <==关键字为case,变量前有$符
"第一个变量内容" <==每个变量内容建议用双引号括起来,关键字则为小括号)
程序段
;; <==每个类别结尾使用两个连续的分号来处理
"第二个变量内容")
程序段
;;
*) <==最后一个变量内容都会用*来代表所有其他值
不包含第一个变量内容与第二个变量内容的其他程序运行段
exit 1
;;
esac <==最终的case结尾!思考一下case反过来写是什么
[root@localhost scripts]# vim sh09-2.sh
#!/bin/bash
# Program:
# Show "Hello" from $1....by using case .... esac
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
case $1 in
"hello")
echo "Hello, how are you ?"
;;
"")
echo "You MUST input parmeters, ex> {$0 someword}"
;;
*) #其实就相当于通配符,0~无穷多个任意字符之意
echo "Usage $0 {hello}"
;;
esac
#运行结果
[root@localhost scripts]# sh sh09-2.sh
You MUST input parmeters, ex> {sh09.sh someword}
[root@localhost scripts]# sh sh09.sh smile
Usage sh09.sh {hello}
[root@localhost scripts]# sh sh09.sh hello
Hello, how are you ?
一般来说,使用“case 变量in” 时,当中的那个“$变量" -般有以下两种取得方式。
#下面以一个例子来进一步说明:让用户能够输人one、two、three, 并且将用户的变量显到屏幕上,如果不是one、two、three,就告诉用户仅有这3种选择。
[root@localhost scripts]# vim sh12.sh
#!/bin/bash
# Program:
# This script only accepts the flowing parameter: one,two or three.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
echo "This program will print your selection !"
# read -p "Input your choice:" choice #暂时取消,可以替换
# case $choice in #暂时取消,可以替换
case $1 in #现在使用,可以用上面两行替换
"one")
echo "Your choice is ONE"
;;
"two")
echo "Your choice is TWO"
;;
"three")
echo "Your choice is THREE"
;;
*)
echo "Usage $O {one|two|three}"
;;
esac
#运行结果
[root@localhost scripts]# sh sh12.sh two
This program will print your selection !
Your choice is TWO
[root@localhost scripts]# sh sh12.sh test
This program will print your selection !
Usage {one|two|three}
#而如果使用互动式时,那么将上面第10, 11 行的#去掉,并将12行加上注解(#),就可以让用户输人参数了。
#格式1:while的含义是....时”所以,这种方式表示“当condition条件成立时,就进行循环,直到condition的条件不成立才停止”的意思。
while [ condition ] <== 中括号内的状态就是判断式
do <==do是循环的开始!
程序段落
done <==done是循环的结束
#格式2:这种方式恰恰与while相反,它表示当condition条件成立时,就终止循环,否则就持续运行循环的程序段。
until [ condition ]
do
程序段落
done
#我们以while来做个简单的练习。假设要让用户输人yes或者是YES才结束程序的运行,否则就一直运行并提示用户输入字符。
[root@localhost scripts]# vim sh13.sh
#!/bin/bash
# Program:
# Repeat question until user input correct answer.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
while [ "$yn" != "yes" -a "$yn" != "YES" ]
do
read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! you input the correct answer."
#运行结果
[root@localhost scripts]# sh sh13.sh
Please input yes/YES to stop this program: yes
OK! you input the correct answer.
#而如果$yn是'yes'或'YES'时,就会离开循环”,那如果使用until呢?
[root@localhost scripts]# vim sh13-2.sh
#!/bin/bash
# Program:
# Repeat question until user input correct answer.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
until [ "$yn" == "yes" -o "$yn" == "YES" ]
do
read -p "Please input yes/YES to stop this program:" yn
done
echo "OK! you input the correct answer."
#运行结果
[root@localhost scripts]# sh sh13-2.sh
Please input yes/YES to stop this program:yes
OK! you input the correct answer.
#如果想要计算+2+3+..+100的值。
[root@localhost scripts]# vim sh14.sh
#!/bin/bash
# Program:
# Use loopto calculate "1+2+3+.. .+100" result.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
s=0 #这是累加的数值变量
i=0 #这是累计的数值,即1, 2,3...
while [ "$i" != "100" ]
do
i=$(($i+1)) #每次i都会添加1
s=$(($s+$i)) #每次都会累加一次
done
echo "The result of '1+2+3+...+100' is ==> $s"
#运行结果
[root@localhost scripts]# sh sh14.sh
The result of '1+2+3+...+100' is ==> 5050
#for循环则是已经知道要进行几次循环。
for var in con1 con2 con3 ...
do
程序段
done
#例1:假设有三种动物,分别是dog、cat、 elephant,如果每一行都要求按“There are dog.."的样式输出,则可以如此撰写程序:
[root@localhost scripts]# vim sh15.sh
#!/bin/bash
# Program:
# Use loopto calculate "1+2+3+.. .+100" result.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
for animal in dog cat elephant
do
echo "There are ${animal}s..."
done
#运行结果
[root@localhost scripts]# sh sh15.sh
There are dogs...
There are cats...
There are elephants...
#例2,由于系统里面的各种账号都是写在/etc/passwd内的第一一 列,能不能在通过管道命令cut找出单纯的账号名称后,以id及finger分别检查用户的识别码与特殊参数?
[root@localhost scripts]# vim /etc/yum.repos.d/dvd.repo
[root@localhost scripts]# mkdir /iso
[root@localhost scripts]# mount /dev/cdrom /iso/
mount: /dev/sr0 is write-protected, mounting read-only
[root@localhost scripts]# yum info finger -y
[root@localhost scripts]# vim sh16.sh
#!/bin/bash
# Program:
# Use id, finger command to check system account's information.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
users=$(cut -d ':' -f1 /etc/passwd) #获取账号名称
for username in $users #开始循环
do
id $username
finger $username
done
#运行上面的脚本,系统账号就会被找出检查
[root@localhost scripts]# sh sh16.sh
#例3,换个角度来看,如果现在需要连串的数字 来进行循环呢?举例来说, 想要利用ping这个可以判断网络状态的命令来进行网络状态的实际检测,要侦测的域是本机所在的192.168.10.1~192.168.10.100。 由于有100台主机,总不会在for后面输人1~ 100吧?
[root@localhost scripts]# vim sh17.sh
#!/bin/bash
# Program:
# Use ping command to check the network' s PC state.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
network="192.168.10" #先定义一个网络号(网络ID)
for sitenu in $(seq 1 100) # seq为sequence (连续)的缩写之意
do
#下面的语句取得ping的回传值是正确的还是失败的
ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1
#开始显示结果是正确的启动(UP )还是错误的没有连通( DOWN )
if [ "$result" == 0]; then
echo "Server ${network} .${sitenu} is UP."
else
echo "Server ${network}.${sitenu} is DOWN."
fi
done
#运行结果
[root@localhost scripts]# sh sh17.sh
sh17.sh: line 15: [: missing `]'
Server 192.168.10.1 is DOWN.
sh17.sh: line 15: [: missing `]'
#例4,如果想要让用户输入某个目录名,然后找出目录内的文件权限
[root@localhost scripts]# vim sh18.sh
#!/bin/bash
# Program:
# Use ping command to check the network' s PC state.
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
#先看看这个目录是否存在
read -p "Please input a directory: " dir
if [ "$dir" == "" -0 ! -d "$dir" ]; then
echo "The $dir is NOT exist in your system."
exit 1
fi
#开始测试文件
filelist=$(ls $dir) #列出所有在该目录下的文件名称
for filename in $filelist
do
perm=""
test -r "$dir/$filename" && perm="Sperm readable"
test -w "$dir/$filename" && perm="$perm writable"
test -x "$dir/$filename" && perm="$perm executable"
echo "The file $dir/$filename's permission is $perm "
done
#运行结果
[root@localhost scripts]# sh sh18.sh
Please input a directory: /root
sh18.sh: line 11: [: too many arguments
The file /root/anaconda-ks.cfg's permission is Sperm readable writable
......
for ((初始值;限制值;执行步长))
do
程序段
done
[root@localhost scripts]# vim sh19.sh
#!/bin/bash
# Program:
# Try do calculate 1+2+....+${your_input}
# History:
# 2023/11/27 Bobby First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
read -p "Please input a number, I will count for 1+2+... +your_input: " nu
s=0
for ((i=1; i<=$nu; i=i+1))
do
s=$(($s+$i))
done
echo "The result of '1+2+3+...+$nu' is ==>$s"
#运行结果
[root@localhost scripts]# sh sh19.sh
Please input a number, I will count for 1+2+... +your_input: 10000
The result of '1+2+3+...+10000' is ==>50005000
不需要运行该script就可以判断出是否有问题:下面就直接以bash的相关参数来进行判断。
[root@localhost scripts]# sh [-nvx] script.sh
选项与参数:
-n:不执行script,仅查询语法的问题。
-V:在执行script前,先将script的内容输出到屏幕上。
-X:将使用到的script内容显示到屏幕上,这是很有用的参数!
#例题1:测试sh16.sh有无语法错误
[root@localhost scripts]# sh -n sh16.sh
#例题2:将sh15.sh的运行过程全部列出来
[root@localhost scripts]# sh -x sh15.sh
+ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
+ export PATH
+ for animal in dog cat elephant
+ echo 'There are dogs...'
There are dogs...
+ for animal in dog cat elephant
+ echo 'There are cats...'
There are cats...
+ for animal in dog cat elephant
+ echo 'There are elephants...'
There are elephants...
ts]# sh sh19.sh
Please input a number, I will count for 1+2+… +your_input: 10000
The result of ‘1+2+3+…+10000’ is ==>50005000
## 8.6 对shell script进行追踪与调试
不需要运行该script就可以判断出是否有问题:**下面就直接以bash的相关参数来进行判断。**
```shell
[root@localhost scripts]# sh [-nvx] script.sh
选项与参数:
-n:不执行script,仅查询语法的问题。
-V:在执行script前,先将script的内容输出到屏幕上。
-X:将使用到的script内容显示到屏幕上,这是很有用的参数!
#例题1:测试sh16.sh有无语法错误
[root@localhost scripts]# sh -n sh16.sh
#例题2:将sh15.sh的运行过程全部列出来
[root@localhost scripts]# sh -x sh15.sh
+ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
+ export PATH
+ for animal in dog cat elephant
+ echo 'There are dogs...'
There are dogs...
+ for animal in dog cat elephant
+ echo 'There are cats...'
There are cats...
+ for animal in dog cat elephant
+ echo 'There are elephants...'
There are elephants...