Bash 脚本编程入门:从基础到高级应用
在 Bash 脚本编程中,有许多实用的技巧和操作方法,下面将为大家详细介绍。
特殊字符与命令替换
在 Bash 脚本里,有一些特殊字符具有特定的功能,例如:
-
\r
:回车符。
-
\t
:水平制表符。
-
\v
:垂直制表符。
命令替换是在脚本中获取变量文本的一种方式。在命令替换时,会使用命令的结果。比如,要让脚本在满足特定条件时才执行,就可以运用此技术(结合
if
条件循环来实现)。使用命令替换时,需将想要使用的命令放在反引号(也叫反撇号)中。示例如下:
nuuk:~/bin # echo "today is `date +%d-%m-%y`"
today is 27-01-09
在这个例子中,
date
命令搭配了特殊的格式化字符,
date +%d-%m-%y
让
date
以“日 - 月 - 年”的格式输出结果。当然,也能把命令替换的结果存到变量里,方便后续在脚本中进行计算。示例代码如下:
nuuk:~/bin # TODAY=`date +%d-%m-%y`
echo today=$TODAY
today is 27-01-09
替换运算符
在脚本里,在脚本继续执行之前,检查变量是否有赋值是很重要的。Bash 提供了替换运算符来实现这一功能。通过使用替换运算符,若变量当前未赋值,可为其赋予默认值等。以下是替换运算符的概述:
| 运算符 | 用途 |
| — | — |
|
${parameter:-value}
| 若参数未定义,则显示该值。 |
|
${parameter=value}
| 若参数根本不存在,则将该值赋给参数;若参数存在但无值,此运算符不做处理。 |
|
${parameter:=value}
| 若参数当前无值或根本不存在,则赋予该值。 |
|
${parameter:?somevalue}
| 若参数不存在或为空,则显示定义为该值的消息,使用此结构会使 shell 脚本立即终止。 |
|
${parameter:+somevalue}
| 若参数有值,则显示该值;若参数无值,则不做任何操作。 |
为了更好地理解替换运算符的工作原理,下面给出一些示例:
1. sander@linux %> echo $BLAH
2.
3. sander@linux %> echo ${BLAH:-variable is empty}
4 variable is empty
5. sander@linux %> echo $BLAH
6.
7. sander@linux %> echo ${BLAH=value}
8. value
9. sander@linux %> echo $BLAH
10. value
11. sander@linux %> BLAH=
12. sander@linux %> echo ${BLAH=value}
13.
14. sander@linux %> echo ${BLAH:=value}
15. value
16. sander@linux %> echo $BLAH
17. value
18. sander@linux %> echo ${BLAH:+sometext}
19. sometext
从这些示例可以看出,不同的替换运算符会根据变量的不同状态产生不同的结果。
模式匹配修改变量内容
替换运算符可在变量无值时进行相应处理,可将其视为脚本中处理错误的一种简单方式。而模式匹配运算符则可在变量中搜索模式,若找到该模式,就对变量进行修改。这非常实用,例如用户输入文件的完整路径名,但脚本仅需文件本身的名称(不包含路径)时,模式匹配运算符就能发挥作用。
以下是一个使用模式匹配运算符的脚本示例:
#!/bin/bash
# stripit
# script that extracts the file name from one that includes the complete path
# usage: stripit <complete file name>
filename=${1##*/}
echo "The name of the file is $filename"
exit 0
执行该脚本时,会得到如下结果:
sander@linux %> ./stripit /bin/bash
the name of the file is bash
模式匹配运算符会尝试定位给定的字符串,在这个例子中是
*/
。
##
用于从字符串开头开始搜索提供字符串的最长匹配,即搜索字符串中最后一个
/
,并移除该
/
及其前面的所有内容。若使用单个
#
,则会从字符串开头搜索最短匹配。
若要从字符串末尾开始搜索,可使用
%
和
%%
,
%
表示最短匹配,
%%
表示最长匹配。以下是相关示例脚本:
#!/bin/bash
# stripdir
# script that isolates the directory name from a complete file name
# usage: stripdir <complete file name>
dirname=${1%%/*}
echo "The directory name is $dirname"
exit 0
不过这个脚本存在问题,可通过以下方式修复:
#!/bin/bash
# stripdir
# script that isolates the directory name from a complete file name
# usage: stripdir <complete file name>
dirname=${1#/}
dirname=${1%%/*}
echo "The directory name is $dirname"
exit 0
脚本中的计算
Bash 提供了一些在脚本中进行计算的方法。虽然不能将其作为电子表格程序的替代品,但进行简单计算还是很有用的。
使用计数器示例
#!/bin/bash
# counter
# script that counts until infinity
counter=1
counter=$((counter + 1))
echo counter is set to $counter
exit 0
此脚本先将变量
counter
初始化为 1,然后将其值加 1,最后显示新值。若将其包含在条件循环中,用于统计在条件为真之前执行的操作次数会更有意义。
使用
expr
命令计算
可使用外部
expr
命令进行各种计算,例如:
sum=`expr 1 + 2`; echo $sum
expr
命令支持多种运算,如下表所示:
| 运算符 | 含义 |
| — | — |
|
+
| 加法(1 + 1 = 2)。 |
|
-
| 减法(10 – 2 = 8)。 |
|
/
| 除法(10 / 2 = 5)。 |
|
*
| 乘法(3 * 3 = 9),但使用时需转义,如
expr 2 \* 2
。 |
|
%
| 取模,计算除法后的余数(11 % 3 = 2)。 |
使用
let
命令计算
let
是内部命令,相比外部的
expr
命令,它可直接从内存加载,无需从硬盘读取。示例如下:
let x="1 + 2"
以下是使用
let
进行计算的脚本示例:
#!/bin/bash
# calcscript
# usage: calc $1 $2 $3
# $1 is the first number
# $2 is the operator
# $3 is the second number
let x="$1 $2 $3"
echo $x
exit 0
另一种计算方式
#!/bin/bash
# calcscript
# usage: calc $1 $2 $3
# $1 is the first number
# $2 is the operator
# $3 is the second number
x=$(($1 $2 $3))
echo $x
exit 0
这里的双括号也可用单对方括号替换(前提是前面有
$
)。
控制结构
在 shell 脚本中,控制命令执行的条件技术称为流控制,也就是控制结构。Bash 提供了多种流控制选项:
-
if
:仅在满足特定条件时执行命令,可结合
else
表示条件不满足时的操作。
-
case
:用于处理选项,让用户在运行命令时进一步指定命令的工作方式。
-
for
:对给定数量的项目运行命令,例如对指定目录中的每个文件执行操作。
-
while
:只要指定条件满足就执行命令,可用于检查主机是否可达或监控进程活动。
-
until
:与
while
相反,直到满足特定条件才停止执行命令。
在详细介绍这些控制结构之前,先介绍
test
命令,它用于进行许多检查,常见的
test
选项如下表所示:
| 选项 | 用途 |
| — | — |
|
test -e $1
| 检查 $1 是否为文件,不考虑文件类型。 |
|
test -f $1
| 检查 $1 是否为普通文件,而非设备文件、目录或可执行文件。 |
|
test -d $1
| 检查 $1 是否为目录。 |
|
test -x $1
| 检查 $1 是否为可执行文件,也可测试其他权限,如
-g
检查 SGID 权限是否设置。 |
|
test $1 -nt $2
| 检查 $1 是否比 $2 新。 |
|
test $1 -ot $2
| 检查 $1 是否比 $2 旧。 |
|
test $1 -ef $2
| 检查 $1 和 $2 是否引用同一个 inode,即一个是否为另一个的硬链接。 |
|
test $1 -eq $2
| 检查整数 $1 和 $2 是否相等。 |
|
test $1 -ne $2
| 检查整数 $1 和 $2 是否不相等。 |
|
test $1 -gt $2
| 若整数 $1 大于整数 $2,则返回真。 |
|
test $1 -lt $2
| 若整数 $1 小于整数 $2,则返回真。 |
|
test $1 -ge $2
| 检查整数 $1 是否大于或等于整数 $2。 |
|
test $1 -le $2
| 检查整数 $1 是否小于或等于整数 $2。 |
|
test -z $1
| 检查 $1 是否为空,用于判断变量是否已定义。 |
|
test $1
| 若 $1 已定义,则返回退出状态 0。 |
|
test $1=$2
| 检查字符串 $1 和 $2 是否相同,用于比较两个变量的值。 |
|
test $1 != $2
| 检查字符串 $1 和 $2 是否不相等,也可使用
!
对其他测试取反。 |
test
命令有两种写法,如
test -f $1
可写成
[ -f $1 ]
,注意方括号之间要有空格。
if ... then ... else
结构
这是流控制的经典示例,结合
test
命令可用于判断文件是否存在、变量是否有值等。以下是一个简单示例:
#!/bin/bash
# testarg
# test to see if argument is present
if [ -z $1 ]
then
echo You have to provide an argument with this command
exit 1
fi
echo the argument is $1
exit 0
若用户未提供参数,
if
循环内的代码会执行,显示提示信息并终止脚本;若提供了参数,则执行
echo the argument is $1
。
if
结构也可更复杂,使用
else
或
elif
测试多个条件。以下是嵌套
if
控制结构的示例:
#!/bin/bash
# testfile
if [ -f $1 ]
then
echo "$1 is a file"
elif [ -d $1 ]
then
echo "$1 is a directory"
else
echo "I don't know what \$1 is"
fi
exit 0
此脚本会检查用户输入的参数,根据不同情况输出相应信息。
if ... then ... else
结构还有另一种写法,使用
&&
和
||
逻辑运算符。例如:
[ -f $1 ] && echo $1 is a file
也可写成:
[ ! -f $1 ] || echo $1 is a file
以下是使用
&&
和
||
重写的脚本示例:
([ -z $1 ] && echo please provide an argument; exit 1) || (([ -f $1 ] && echo $1 is a file) || ([ -d $1 ] && echo $1 is a directory || echo I have no idea what $1 is))
再看其他
if ... then ... else
的示例:
rsync -vaze ssh --delete /srv/ftp 10.0.0.20:/srv/ftp || echo "rsync failed" | mail admin@mydomain.com
此命令尝试同步目录内容,若失败则向用户发送消息。
if [ `df -m /var | tail -n1 | awk '{print $4} '` -lt 120 ]
then
logger running out of disk space
fi
此脚本检查
/var
挂载点的可用磁盘空间是否低于 120MB,若低于则记录日志。也可重写为:
[ `df -m /var | tail -n1 | awk '{print $4}'` -lt $1 ] && logger running out of disk space
总之,编写 shell 脚本很有趣,因为总能找到更好的实现方式。
Bash 脚本编程入门:从基础到高级应用
case
控制结构
case
结构用于处理多种选项的情况,允许用户在运行脚本时进一步明确命令的执行方式。以下是
case
结构的基本语法:
case $variable in
pattern1)
commands1
;;
pattern2)
commands2
;;
...
*)
default_commands
;;
esac
下面是一个简单的
case
结构示例脚本:
#!/bin/bash
# testcase
# 根据用户输入选择不同操作
echo "请输入一个字母 (a, b, c):"
read choice
case $choice in
a)
echo "你选择了 a"
;;
b)
echo "你选择了 b"
;;
c)
echo "你选择了 c"
;;
*)
echo "无效的选择"
;;
esac
exit 0
在这个脚本中,用户输入一个字母,脚本会根据输入执行相应的操作。如果输入的不是
a
、
b
或
c
,则会输出“无效的选择”。
for
循环
for
循环用于对给定的一组项目依次执行命令。例如,要对指定目录中的每个文件执行操作,可以使用
for
循环。以下是
for
循环的基本语法:
for variable in list
do
commands
done
以下是一个简单的
for
循环示例脚本,用于列出指定目录中的所有文件:
#!/bin/bash
# listfiles
# 列出指定目录中的所有文件
directory="/home/user/Documents"
for file in $directory/*
do
echo $file
done
exit 0
在这个脚本中,
for
循环会遍历
/home/user/Documents
目录下的所有文件,并将文件名依次输出。
while
循环
while
循环会在指定条件满足时持续执行命令。它非常适合用于检查某个主机是否可达,或者监控某个进程的活动。以下是
while
循环的基本语法:
while condition
do
commands
done
以下是一个使用
while
循环和计数器的示例脚本,用于统计在某个条件为真之前执行的操作次数:
#!/bin/bash
# whilecounter
# 统计操作次数,直到计数器达到 5
counter=1
while [ $counter -le 5 ]
do
echo "当前计数器值: $counter"
counter=$((counter + 1))
done
exit 0
在这个脚本中,
while
循环会不断检查
counter
的值是否小于等于 5,如果是,则输出当前计数器的值,并将计数器加 1,直到计数器的值大于 5 时停止循环。
until
循环
until
循环与
while
循环相反,它会一直执行命令,直到指定的条件满足为止。以下是
until
循环的基本语法:
until condition
do
commands
done
以下是一个使用
until
循环的示例脚本,用于在计数器达到 5 之前不断输出计数器的值:
#!/bin/bash
# untilcounter
# 统计操作次数,直到计数器达到 5
counter=1
until [ $counter -gt 5 ]
do
echo "当前计数器值: $counter"
counter=$((counter + 1))
done
exit 0
在这个脚本中,
until
循环会不断检查
counter
的值是否大于 5,如果否,则输出当前计数器的值,并将计数器加 1,直到计数器的值大于 5 时停止循环。
控制结构的综合应用
在实际的脚本编写中,常常需要综合使用多种控制结构来实现复杂的功能。以下是一个综合使用
if
、
for
和
while
循环的示例脚本,用于查找指定目录下所有大于 1MB 的文件,并将它们移动到另一个目录:
#!/bin/bash
# find_and_move
# 查找指定目录下所有大于 1MB 的文件,并将它们移动到另一个目录
source_dir="/home/user/Documents"
destination_dir="/home/user/LargeFiles"
for file in $source_dir/*
do
if [ -f $file ]
then
size=$(du -m $file | cut -f1)
while [ $size -gt 1 ]
do
mv $file $destination_dir
echo "已将 $file 移动到 $destination_dir"
break
done
fi
done
exit 0
在这个脚本中,
for
循环会遍历
source_dir
目录下的所有文件,
if
语句会检查当前项是否为文件,如果是,则使用
du
命令获取文件的大小(以 MB 为单位),并使用
while
循环检查文件大小是否大于 1MB,如果是,则将文件移动到
destination_dir
目录,并输出相应的信息。
总结
通过本文的介绍,我们学习了 Bash 脚本编程中的许多重要概念和技术,包括特殊字符、命令替换、替换运算符、模式匹配、计算方法以及各种控制结构。这些知识和技能可以帮助我们编写更加灵活、高效的脚本,实现各种自动化任务。在实际应用中,我们可以根据具体需求选择合适的技术和方法,综合运用各种控制结构来实现复杂的功能。同时,不断练习和实践也是提高脚本编程能力的关键,希望大家能够通过不断的学习和实践,掌握 Bash 脚本编程的精髓,为自己的工作和学习带来便利。
下面用 mermaid 流程图展示一个简单的
if...else
判断文件类型脚本的执行流程:
graph TD
A[开始] --> B{输入文件路径}
B --> C{文件是否存在}
C -- 是 --> D{是否为普通文件}
D -- 是 --> E[输出 "是普通文件"]
D -- 否 --> F{是否为目录}
F -- 是 --> G[输出 "是目录"]
F -- 否 --> H[输出 "未知类型"]
C -- 否 --> I[输出 "文件不存在"]
E --> J[结束]
G --> J
H --> J
I --> J
再用表格总结不同控制结构的特点和适用场景:
| 控制结构 | 特点 | 适用场景 |
| — | — | — |
|
if...then...else
| 根据条件判断执行不同代码块,可嵌套 | 判断文件是否存在、变量是否有值等简单条件判断 |
|
case
| 处理多种选项情况 | 用户输入多种选择的场景 |
|
for
| 对给定一组项目依次执行命令 | 遍历目录下的文件、处理数组元素等 |
|
while
| 条件满足时持续执行命令 | 检查主机是否可达、监控进程活动等 |
|
until
| 条件满足时停止执行命令 | 与
while
相反的场景,如等待某个条件达成 |
超级会员免费看
33万+

被折叠的 条评论
为什么被折叠?



