一文带你精通 Linux Shell 编程
一、Shell 编程简介
1.1 什么是 Shell
从程序员的角度来看,Shell 是用 C 语言编写的程序;从用户的角度来看,它是与 Linux 操作系统沟通的桥梁。用户既可以输入命令立即执行,也能通过 Shell 脚本编程来完成复杂的操作。在 Linux 系统管理领域,Shell 编程至关重要,是每个 Linux 用户的必修课。
1.2 Shell 种类
Linux 的 Shell 种类繁多,常见的包括:
- Bourne Shell(
/usr/bin/sh或/bin/sh) - Bourne Again Shell(
/bin/bash) - C Shell(
/usr/bin/csh) - K Shell(
/usr/bin/ksh) - Shell for Root(
/sbin/sh)
不同 Shell 的语法略有差异,基本掌握一种即可。本文重点关注 Bash(Bourne Again Shell),因其易用且免费,在日常工作中广泛使用,也是多数 Linux 系统默认的 Shell。
1.3 Shell 脚本格式
使用 vi 等文本编辑器编写 Shell 脚本时,格式相对固定:
- Shebang 行:首行通常为
#!/bin/sh或#!/bin/bash,用于指定解释脚本的 Shell 程序。缺少此行可能导致执行出错。 - 主程序:后续内容为脚本主体,以
#开头的行为注释行。 - 续行:若一行未写完,可在行尾加
\与下一行合并。 - 保存与执行:编辑完成后,将脚本存为
filename.sh(后缀表明是 Bash 脚本)。执行前需用chmod +x filename.sh赋予可执行权限,然后通过./filename.sh执行。
1.4 变量赋值与取值
Shell Script 是弱类型语言,使用变量无需声明类型。
- 赋值:格式为
variable_name=variable_value(注意:等号两侧不能有空格)。对已有值的变量赋值会覆盖旧值。 - 取值:在变量名前加
$,如$variable_name。变量可在引号中使用。 - 区分边界:若有混淆,可用花括号区分,如
echo "Hi, ${a}s"。 - 引号区别:单引号中的变量不进行替换操作,双引号中会替换。
1.5 相关 Linux 命令
- env:显示用户环境区中的变量及其取值。
- set:显示本地数据区和用户环境区中的变量及其取值。
- unset:删除指定变量当前取值,使其为空。
- export:将本地数据区中的变量转移到用户环境区(使其成为环境变量)。
二、Shell 编程语法
2.1 if 语句
if 语句用于流程控制,基本语法如下:
if 条件; then
命令序列
elif 条件; then
命令序列
else
命令序列
fi条件部分常用分号分隔,常用条件测试包括:
- 文件判断:
[ -f "$file" ]判断$file是否为普通文件。 - 数值比较:
[ $a -lt 3 ]判断$a值是否小于 3,-gt和-le分别表示大于和小于等于。 - 权限判断:
[ -x "$file" ]判断$file是否存在且有可执行权限,-r测试文件可读性。 - 变量判断:
[ -n "$a" ]判断变量$a是否有值,-z测试空串。 - 字符串判断:
[ "$a" = "$b" ]判断$a和$b取值是否相等。 - 逻辑判断:
[ cond1 -a cond2 ]判断 cond1 和 cond2 是否同时成立,-o表示 cond1 和 cond2 有一个成立。
注意:条件测试部分空格很重要,方括号两侧、-f、-lt、= 等符号两侧都要有空格,否则 Shell 解释脚本会出错。
2.2 Here 文档
Here 文档用于将多行文本传递给命令,格式以 << 开始,后跟标识字符串,文档结束时该字符串也要出现。例如:
cat <<HELP
ren -- renames a number of files using sed regular expressions
USAGE:
ren 'regexp' 'replacement' files
EXAMPLE:
rename all *.HTM files in *.html:
ren 'HTM$' 'html' *.HTM
HELP2.3 循环语句
Shell Script 中的循环有以下几种格式:
while 循环:
while [ 条件 1 ] && [ 条件 2 ] …; do 命令序列 donefor 循环:
for var in 列表; do 命令序列 donefor 循环(C 语言风格):
for (( 条件 1; 条件 2; 条件 3 )); do 命令序列 doneuntil 循环:
until [ 条件 1 ]; do 命令序列 done
循环中可使用 break 和 continue 中断操作。例如:
Sum=0
i=0
while [ $i != "100" ]
do
i=`expr $i + 1`
Sum=`expr $Sum + $i`
done
echo $i $Sum2.4 case 语句
case 语句类似 C 语言多分支结构,语法如下:
case var in
模式 1 )
命令序列 ;;
模式 2 )
命令序列 ;;
*)
默认命令序列 ;;
esac例如:
while getopts vc: OPTION
do
case $OPTION in
c) COPIES=$OPTARG
echo "$COPIES";;
v) echo "suyang";;
\?) exit 1;;
esac
done2.5 select 扩展
用于交互式应用,语法如下:
select var in 列表; do
break
done例如:
#!/bin/bash
echo "Your choice?"
select var in "a" "b" "c"; do
break
done
echo $var2.6 函数定义
语法形式如下:
functionname()
{
命令序列
}函数中处理参数方法与脚本参数相同,用 $1、$2 表示第一、第二个参数,$* 表示参数列表。
2.7 脚本调试
- 使用 echo 输出变量取值:简单查看变量值。
- 使用 -x 参数:执行脚本并显示所有变量取值,如
sh -x filename.sh。 - 使用 -n 参数:不执行脚本,仅检查语法错误。
三、特殊变量与字符
3.1 系统环境变量
- $HOME:使用者自己的目录。
- $PATH:执行命令时搜寻的目录。
- $TZ:时区。
- $MAILCHECK:检查新信件的间隔秒数。
- $PS1:命令列提示号。
- $PS2:命令未打完时,Shell 要求再输入的提示号。
- $MANPATH:man 指令的搜寻路径。
3.2 特殊变量
- $0:程序执行名字。
- $n:程序的第 n 个参数值(n = 1..9)。
- $* 和 $@:程序的所有参数(略有区别,
$*将所有参数视为一个整体,$@将每个参数视为独立个体)。 - $#:程序的参数个数。
- $$:程序的 PID。
- $!:执行上一个指令的 PID。
- $?:执行上一个指令的返回值。
3.3 Shell 中的变元
- *:任意字符串。
- ?:一个任意字符。
- [abc]:a、b、c 三者中之一。
- [a-n]:从 a 到 n 的任一字符。
3.4 特殊字符表示
- \b:退回。
- \c:打印一行时无换行符。
- \f:换页。
- \r:回车。
- \t:制表。
- \v:垂直制表。
- \:反斜线本身。
3.5 判断文件属性
格式为 -操作符 filename,常见操作符有:
- -e:文件存在返回 0(真),否则返回 1(假)。
- -r:文件可读返回 0,否则返回 1。
- -w:文件可写返回 0,否则返回 1。
- -x:文件可执行返回 0,否则返回 1。
- -o:文件属于用户本人返回 0,否则返回 1。
- -s:文件长度大于 0 返回 0,否则返回 1。
- -f:文件为普通文件返回 0,否则返回 1。
- -d:文件为目录文件时返回 0,否则返回 1。
3.6 测试字符串
- 字符串 1 = 字符串 2:两字串相等为真。
- 字符串 1 != 字符串 2:两字串不等为真。
- -n 字符串:字符串长度大于 0 为真。
- -z 字符串:字符串长度为 0 为真。
- 字符串(直接判断):字符串非空为真。
3.7 测试两个整数关系
- 数字 1 -eq 数字 2:两数相等为真。
- 数字 1 -ne 数字 2:两数不等为真。
- 数字 1 -gt 数字 2:数字 1 大于数字 2 为真。
- 数字 1 -ge 数字 2:数字 1 大于等于数字 2 为真。
- 数字 1 -lt 数字 2:数字 1 小于数字 2 为真。
- 数字 1 -le 数字 2:数字 1 小于等于数字 2 为真。
3.8 逻辑测试
- -a:与(AND)。
- -o:或(OR)。
- !:非(NOT)。
3.9 特殊字符引用
$ 符号
echo $?:显示上一条指令退出状态,判断上条命令执行是否成功。echo "$?":效果同上。echo '$?':显示$?字符串。echo \$?:显示$?字符串。echo "\$?":显示$?字符串。双引号中 $ 符号有特殊意义,单引号可屏蔽特殊字符意义,反斜杠也可屏蔽特殊字符特殊含义。
- \ 反斜杠
反斜杠可屏蔽特殊符号特殊含义,如echo \$A显示$A,echo \` 显示`,echo \"显示双引号,echo \\显示\。 - ` 反引号
反引号用于命令替换,将其中字符串作为命令执行,结果赋给变量,如A=date`,echo $A` 显示当时时间串。 - " 双引号"
双引号可避免引用特殊字符,但部分特殊字符仍有特殊含义,如$、\、`、"。单引号引起内容原样输出,双引号引起内容可能有特殊处理。要输出特殊字符原形,可用单引号或反斜杠,如echo '"'输出",echo "\""也输出"。 - 其它特殊字符
<、>、*、?、[、]等特殊字符在双引号中可能有特殊含义,输出原形可用单引号或双引号引起来。
3.10 条件测试语句
if 条件语句
格式:if 条件表达式 then # 当条件为真时执行以下语句 命令列表 else # 为假时执行以下语句 命令列表 fiif 语句可嵌套,如:
if test -f "$1" then lpr $1 else if test -d "$1" then cd $1 lpr $1 else echo "$1 不是文件或目录" fi fi也可改为:
if test -f "$1" then lpr $1 elif test -d "$1" # elif 同 else if then (cd $1; lpr $1) else echo "$1 不是文件或目录" fi多重条件测试语句 case
格式:case 字串 in 模式) 命令列表 ;; 模式) 命令列表 ;; .... esac例如:
case $1 in *.c) cc $1 ;; *.txt) lpr $1 ;; *) echo "未知的类型" ;; esac
3.11 循环语句
while 循环
命令格式:while 条件表 do 命令表 done执行过程:先执行条件表,若最后一条语句退出状态为零(条件为真),执行循环体内命令表,然后再次检查条件表,如此循环,直到条件表最后一条语句退出状态非零。例如:
Sum=0 i=0 while true # true 是系统关键词,表示真 do i=`expr $i + 1` Sum=`expr $Sum + $i` if [ $i = "100" ] then break fi done echo $i $Sum也可改为:
Sum=0 i=0 while [ $i != "100" ] do i=`expr $i + 1` Sum=`expr $Sum + $i` done echo $i $Sum还可用 until 作为测试条件,与 while 相反,当条件为假时循环,如:
Sum=0 i=0 until [ $i = "100" ] do i=`expr $i + 1` Sum=`expr $Sum + $i` done echo $i $Sumfor 循环
命令格式:for 变量 in 名字列表 do 命令列表 done名字列表是空格分隔字符串列表,Shell 每次从列表取一个字符串赋给循环变量。例如:
for File in a1 a2 a3 a4 a5 do diff aa/$File bb/$File done也可省略 in 名字列表部分,用当前位置参数代替,如:
for File do echo $File done循环控制语句
- break:不执行当前循环体内 break 下面语句,直接退出当前循环。
- continue:忽略本循环体内 continue 下面语句,从循环头开始执行。
3.12 命令组合
圆括号
圆括号使 Shell 创建子 Shell 读取并执行括起来的命令,创建子进程运行组合程序,子进程操作不影响当前 Shell 变量值,且当前 Shell 用 export 输出的变量在子 Shell 中有效。例如:(cd newdir; ls)注意圆括号在命令行中的特殊组合意义,若要输出其原义,需用双引号括起来,如
echo "a(b)"。花括号
花括号由当前 Shell 读取并执行括起来的命令,不创建子 Shell。左右花括号作为一条命令第一个字出现时才有特殊含义。例如:{ cd olddir; ls; }退出状态等于最后一条括起来命令的退出状态。
3.13 可在当前 Shell 中执行的命令
包括 break、case、cd、continue、echo、eval、exec、exit、export、for、if、read、readonly、return、set、shift、test、times、trap、umask、until、wait、while、:、{} 等。
四、实战案例
4.1 批量文件重命名脚本
以下脚本实现批量文件重命名功能,将指定扩展名的文件改为新的扩展名。
#!/bin/bash
# 检查参数个数
if [ $# -lt 2 ]; then
cat <<HELP
Usage: rename.sh old_extension new_extension [files]
HELP
exit 1
fi
OLD_EXT="$1"
NEW_EXT="$2"
shift
shift
# 循环处理文件
for file in $*; do
if [ -f "$file" ]; then
newfile=`echo "$file" | sed "s/${OLD_EXT}/${NEW_EXT}/g"`
if [ -f "$newfile" ]; then
echo "ERROR: $newfile exists already"
else
echo "Renaming $file to $newfile"
mv "$file" "$newfile"
fi
fi
done使用方法:
./rename.sh txt log file1.txt file2.txt file3.txt4.2 简单的备份脚本
该脚本用于备份指定目录到指定备份目录,备份文件以日期命名。
#!/bin/bash
SOURCE_DIR="/home/user/data"
BACKUP_DIR="/home/user/backup"
DATE=$(date +%Y%m%d)
# 创建备份目录(若不存在)
if [ ! -d "$BACKUP_DIR" ]; then
mkdir -p "$BACKUP_DIR"
fi
# 备份文件
tar -czf "$BACKUP_DIR/data_$DATE.tar.gz" "$SOURCE_DIR"
echo "Backup completed successfully to $BACKUP_DIR/data_$DATE.tar.gz"4.3 系统信息收集脚本
此脚本收集系统的一些基本信息,如内存使用情况、磁盘使用情况等。
#!/bin/bash
# 内存使用情况
MEM_INFO=$(free -m)
echo "Memory Information:"
echo "$MEM_INFO"
# 磁盘使用情况
DISK_INFO=$(df -h)
echo "Disk Information:"
echo "$DISK_INFO"
# 当前登录用户
USER_INFO=$(who)
echo "Logged in Users:"
echo "$USER_INFO"五、总结
Shell 编程在 Linux 系统管理和自动化任务处理中具有不可替代的作用。通过掌握 Shell 脚本的基本语法、特殊变量、条件判断、循环结构以及各种命令的组合使用,能够高效地完成各种复杂任务,提高工作效率。无论是系统管理员还是普通 Linux 用户,深入学习 Shell 编程都将为更好地使用和管理 Linux 系统提供有力支持。不断实践和探索,能够发现 Shell 编程更多的强大功能,让 Linux 系统为我们提供更优质的服务。
说明:本文示例主要基于 Bash 环境。部分命令(如expr)属于较早期的 POSIX 标准写法,现代 Bash 脚本中推荐使用$(( ))进行算术运算。文中涉及的文件测试操作符及语法在大多数 Linux 发行版默认 Bash 版本中通用。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/yi-wen-dai-ni-jing-tong-linux-shell-bian-cheng.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。