Linux Shell脚本编程
将Linux命令组合成一个文本文件,在须要运行这些命令时,只用运行这个文本即可
创建文件
在shell脚本文件的第一行指定要使用的shell: #!/bin/bash
在通常的shell脚本中,#用作注释行,shell不会处理脚本中的注释行,除了第一行。#后面的!告诉系统用哪个shell解释器
例如,在命令行中输入vim test.sh
#!/bin/bash
date
who
上述脚本等价于date;who
,使用了;将命令组合并按顺序执行
保存文件并退出vim,注意此时在命令行里输入./test.sh
,并不能直接运行:
$ ./test.sh
bash: ./test.sh: Permission denied
因为没有执行权限:
$ ls -l test.sh
-rw-rw-r-- 1 hwx hwx 21 Sep 1 11:34 test.sh
使用chmod修改权限后运行:
$ chmod u+x test.sh
$ ./test.sh
Thu 01 Sep 2022 11:43:55 AM CST
root :0 2022-09-01 08:45 (:0)
使用变量
通过变量可以临时性的将信息存储在shell脚本中
环境变量
shell维护着一组环境变量,用来记录特定的系统信息。比如系统的名称、登录到系统上的用户名、用户的系统ID(也叫UID)、用户默认的主目录以及shell查找程序的搜索路径,可以用set命令来显示一份完整的当前变量列表:
$ set
BASH=/usr/bin/bash
BASHOPTS=checkwinsize:cmdhist:complete_fullquote:expand_aliases:extglob:extquote:force_fignore:globasciiranges:histappend:interactive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES=()
....
使用美元符号加变量名可以使用这些环境变量,编写如下shell脚本:
#!/bin/bash
echo "User info for userid: $USER"
echo UID: $UID
echo HOME: $HOME
运行输出:
$ ./test.sh
User info for userid: hwx
UID: 1000
HOME: /home/hwx
echo命令中的环境变量会在脚本运行时替换成当前的值,放在双引号中同样起作用,所以要显示美元符得用\
转义
用户变量
除了环境变量,shell脚本还允许定义和使用自己的变量,临时存储数据并在整个脚本中使用,使用等号将值赋给用户变量,变量、等号和值之间不能有空格
#!/bin/bash
var1=10
var2=-20
var3=test
var4="just a test"
引用一个变量时需要美元符,但引用变量来赋值时不使用美元符号
#!/bin/bash
value1=10
value2=$value1
echo $value2
value2=value1
echo $value2
运行:
$ ./test.sh
10
value1
使用$才能将value1的值赋值给value2,否则只是一个字符串
命令替换
有时要从命令输出中提取信息,并将其赋值给变量,把输出赋给变量后就可以随意在脚本中使用,使用反引号或$()
可以从命令中提取提示信息:
#!/bin/bash
test=$(date)
echo "The date and time are: " $test
等价于:
#!/bin/bash
echo "The date and time are: " `date`
重定向输入输出
将命令的输出重定向到另一个位置(比如文件,而非显示器),也可以用于输入
输出重定向
最基本的重定向将命令的输出发送到一个文件中,bash shell用大于号(>)来完成这个功能:command > outputfile
输出到标准输出的命令输出就会被保存到指定的输出文件中,如下所示:
$ date > test.txt
$ cat test.txt
Thu 01 Sep 2022 02:43:53 PM CST
重定向操作符创建了一个文件(默认的权限掩码),并将date命令的输出重定向到该文件中。若文件已经存在,则会将原有的覆盖,使用>>便不会覆盖,而是追加数据
$ date >> test.txt
$ cat test.txt
Thu 01 Sep 2022 02:43:53 PM CST
Thu 01 Sep 2022 02:47:26 PM CST
输入重定向
输入重定向是将文件的内容重定向到命令,而非将命令输出重定向到文件,输入重定向符号是小于号(<): command < inputfile
$ wc < test.txt
2 14 64
wc命令的作用是对文本文件进行计数,默认输出: 行数 词数 字节数
通过将文本文件重定向到wc命令,立刻就可以得到文件中的行、词和字节的计数
另外还有内联输入重定向,这种方法不需要使用文件进行重定向,只需在命令行中指定用于输入重定向,使用远小于号(<<),同时必须指定一个文本标记来划分输入数据的开始和结尾:
$ wc << EOF
> Hello
> World
> EOF
2 2 12
管道
将一个命令的输出作为另一个命令的输入,这可以用重定向来实现,也可以用管道: command1 | command2
在进程中匹配含有http的进程:
$ ps -af | grep http
hwx 25820 21362 0 15:36 pts/2 00:00:00 grep --color=auto http
ps -af输出的进程列表被输入给grep http
结构化命令
许多程序要求对shell脚本中的命令施加一些逻辑流程控制
if-then语句
最基本的结构化命令,格式如下:
if command
then
commands
fi
bash shell的if语句会运行if后面的命令,如果该命令的退出码是0,则位于then部分的命令就会被执行,其他情况下就不会执行then部分,fi表示到此结束
如下一段代码所示:
#!/bin/bash
if pwd
then
echo "It worked"
fi
运行:
$ ./test.sh
/home/hwx
It worked
如果pwd命令成功结束则返回0,echo语句就会显示该文本字符串
如下if后是一条不存在的命令:
#!/bin/bash
if UnknowCommand
then
echo "It worked"
fi
echo "finish"
运行:
$ ./test.sh
./test.sh: line 2: UnknowCommand: command not found
finish
then没有被运行
if-then-else语句
如果命令遇到一个非0退出码,让bash shell继续执行脚本中的另一组命令便可借助这个命令
示例:
#!/bin/bash
testuser=NoSuchUser
if grep $testuser /etc/passwd
then
echo "The bash files for user $testuser are:"
ls -a /home/$testuser/.b*
echo
else
echo "The user $testuser does not exist on this system"
echo
fi
运行:
$ ./test.sh
The user NoSuchUser does not exist on this system
指定的用户名并不存在,所以得到一个非0退出,相应的就执行else语句
嵌套if语句
if语句可以嵌套使用,如下所示:
#!/bin/bash
testuser=NoSuchUser
if grep $testuser /etc/passwd
then
echo "The bash files for user $testuser are:"
ls -a /home/$testuser/.b*
echo
else
echo "The user $testuser does not exist on this system"
if ls -d /home/$test
then
echo "However, $testuser has a dictory."
fi
fi
运行:
$ ./test.sh
The user NoSuchUser does not exist on this system
ls: cannot access '/home/NoSuchUser': No such file or directory
这段程序通过查看/etc/passwd检查是否存在某个用户名,之后通过ls -d检查该用户的目录是否存在
可以将elif语句串起来,形成一个if-elif嵌套组合:
#!/bin/bash
testuser=NoSuchUser
if grep $testuser /etc/passwd
then
echo "The bash files for user $testuser are:"
elif ls -d /home/testuser
then
echo "the user $testuser does not exist on this system"
echo "However, $testuser has a dictory"
else
echo "The user $testuser does not exist on this system"
echo "However, $testuser has a dictory"
fi
运行:
$ ./test.sh
ls: cannot access '/home/testuser': No such file or directory
The user NoSuchUser does not exist on this system
However, NoSuchUser does not have a dictory
要注意的是,elif语句中,紧跟其后的else语句属于elif代码块,并不属于之前的if-then代码
test命令
test命令中列出的条件如果成立,test命令就会退出并返回状态码0: test condition
与if-then配合使用:
if test condition
then
commands
fi
示例:
#!/bin/bash
value="test"
if test $value
then
echo "True"
else
echo "False"
fi
还有一种与test相同的语句,用于条件测试:
if [ conditions ]
then
commands
fi
方括号中定义了测试条件,第一个方括号之后和第二个方括号之前必须加上一个空格
#!/bin/bash
v1=10
v2=11
if [ $v1 -gt 5 ]
then
echo "The test value $v1 is greater than 5"
fi
if [ $v1 -eq $v2 ]
then
echo "The value are equal"
else
echo "The value are different"
fi
运行:
$ ./test.sh
The test value 10 is greater than 5
The value are different