shell学习从入门到精通
bShell 语法学习:从入门到精通
Shell 是一个命令解释器,它充当用户与操作系统内核之间的接口。Shell 脚本(Shell Script)则是由一系列 Shell 命令组成的文本文件,用于自动化执行任务。最常用的 Shell 是 Bash (Bourne-Again SHell
),本指南将主要以 Bash 为例。
第一部分:入门基础
1. "Hello World" - 你的第一个脚本
学习任何语言的第一步都是 "Hello World"。
脚本内容 (
hello.sh
):#!/bin/bash# 这是一个注释。井号(#)后面的内容会被忽略。 echo "Hello, World!"
关键点解释:
#!/bin/bash
: 这叫做 "Shebang"。它告诉系统这个脚本应该使用哪个解释器来执行,这里是/bin/bash
。#
: 这是注释符号。echo
: 这是最常用的命令之一,用于在终端输出文本或变量内容。
如何执行脚本:
方法一:作为
bash
的参数bash hello.sh
方法二:赋予执行权限后直接运行(推荐)
添加执行权限:
chmod +x hello.sh
执行脚本:
./hello.sh
(./
表示当前目录)
2. 变量 (Variables)
变量用于存储数据。Bash 中的变量不需要事先声明类型。
定义和使用:
规则:变量名、等号和值之间 不能有空格。
引用:使用
$
符号。推荐用双引号""
包裹变量,以避免空格等特殊字符引起的问题。
#!/bin/bash# 定义变量 NAME="Alice" AGE=30# 使用变量 echo "My name is $NAME, and I am $AGE years old."# 推荐使用花括号{}来明确变量边界 echo "This is ${NAME}'s book."
只读变量:
CITY="New York" readonly CITY # 下面这行会报错: CITY: readonly variable # CITY="London"
接收用户输入 (
read
):#!/bin/bashecho "What is your name?" read USER_NAME echo "Hello, $USER_NAME!"# -p 可以在同一行显示提示信息 read -p "What is your favorite color? " COLOR echo "I see, your favorite color is $COLOR."
2.1. 变量引用
- 语法:
$变量名
或${变量名}
(推荐后者,避免歧义)
#!/bin/bashname="Alice"
echo "Name: $name" # 输出:Name: Alice
echo "Name: ${name}" # 输出:Name: Alice(推荐)# 变量拼接(无需连接符)
greeting="Hello, ${name}!"
echo $greeting # 输出:Hello, Alice!# 变量作为命令参数
file="test.txt"
cat ${file} # 等价于 cat test.txt
2.2. 特殊变量(预定义变量)
Shell 有一系列内置变量,用于获取脚本运行时的信息:
变量 | 含义 | 示例(执行 ./script.sh arg1 arg2 ) |
---|---|---|
$0 | 脚本文件名 | echo $0 → ./script.sh |
$n | 第 n 个参数(n≥1) | echo $1 → arg1 ;echo $2 → arg2 |
$# | 参数总数 | echo $# → 2 |
$* | 所有参数(作为单个字符串) | echo $* → arg1 arg2 |
$@ | 所有参数(作为独立字符串) | for i in $@; do echo $i; done → 分别输出 arg1、arg2 |
$? | 上一条命令的退出状态(0 成功,非 0 失败) | ls non_exist; echo $? → 2 (失败) |
$$ | 当前脚本的进程 ID(PID) | echo $$ → 12345 (当前进程 ID) |
$! | 上一个后台进程的 PID | sleep 10 &; echo $! → 后台进程的 PID |
#!/bin/bash
# 保存为 args.sh,执行:./args.sh a b cecho "脚本名:$0" # 输出:脚本名:./args.sh
echo "第1个参数:$1" # 输出:第1个参数:a
echo "参数总数:$#" # 输出:参数总数:3
echo "所有参数(\$*):$*" # 输出:所有参数($*):a b c# 遍历 $@(每个参数独立处理)
echo "遍历 \$@:"
for arg in "$@"; doecho "- $arg"
done
# 输出:
# 遍历 $@:
# - a
# - b
# - c
2.3. 环境变量
环境变量是全局变量,用于配置系统环境(如 PATH
、HOME
),可通过 export
声明。
#!/bin/bash# 查看环境变量(已存在的)
echo "HOME: $HOME" # 输出:HOME: /home/user(当前用户家目录)
echo "PATH: $PATH" # 输出:系统命令搜索路径# 定义并导出环境变量(子进程可访问)
export MY_VAR="hello"
# 在当前终端执行脚本后,可通过 echo $MY_VAR 查看(仅当前终端有效)# 临时设置环境变量(仅当前命令有效)
MY_VAR="test" echo $MY_VAR # 输出:test
3. 命令替换
将一个命令的输出结果赋值给一个变量。
语法:
$(command)
(推荐) 或`command`
(旧式)#!/bin/bashCURRENT_DATE=$(date "+%Y-%m-%d %H:%M:%S") FILES_IN_DIR=$(ls -l)echo "Current time is: $CURRENT_DATE" echo "---" echo "Files in the current directory:" echo "$FILES_IN_DIR"
4. 数据类型与扩展
Shell 没有严格的数据类型,但支持字符串、数字、数组、关联数组(字典)。
1. 字符串
Shell 字符串可用单引号 ''
或双引号 ""
,区别:
- 单引号:不解析变量,不转义特殊字符(除单引号本身)。
- 双引号:解析变量,可转义特殊字符(如
\n
、\t
)。
#!/bin/bashname="Alice"# 单引号(变量不解析)
echo 'Hello, $name' # 输出:Hello, $name# 双引号(变量解析)
echo "Hello, $name" # 输出:Hello, Alice# 转义字符(需双引号)
echo "Line1\nLine2" # 输出:Line1\nLine2(默认不转义)
echo -e "Line1\nLine2" # 加 -e 启用转义,输出:
# Line1
# Line2# 字符串长度
str="hello"
echo ${#str} # 输出:5# 字符串截取(${变量:起始位置:长度},起始位置从0开始)
echo ${str:1:3} # 从索引1开始,取3个字符 → ell
5. 运算符与表达式
Shell 支持算术运算、字符串运算、逻辑运算,需用特定语法(如 $((...))
)。
1. 算术运算符
语法:$((表达式))
或 $[表达式]
(推荐前者),支持 +
、-
、*
、/
、%
(取余)、**
(幂)。
#!/bin/basha=10
b=3# 加法
echo $((a + b)) # 输出:13# 乘法(注意:* 无需转义,在 $((...)) 中直接用)
echo $((a * b)) # 输出:30# 取余
echo $((a % b)) # 输出:1# 幂运算(Bash 4.0+ 支持)
echo $((2 **3)) # 输出:8# 赋值运算(在表达式中修改变量)
$((a += 5)) # 等价于 a = a + 5
echo $a # 输出:15
2. 比较运算符(数字)
用于条件判断(if
语句中),语法:[ 数字1 运算符 数字2 ]
或 (( 数字1 运算符 数字2 ))
。
运算符 | 含义 | 示例(a=10, b=5) |
---|---|---|
-eq | 等于 | [ $a -eq $b ] → false |
-ne | 不等于 | [ $a -ne $b ] → true |
-gt | 大于 | [ $a -gt $b ] → true |
-lt | 小于 | [ $a -lt $b ] → false |
-ge | 大于等于 | (( a >= b )) → true |
-le | 小于等于 | (( a <= b )) → false |
#!/bin/basha=10
b=5if [ $a -gt $b ]; thenecho "$a 大于 $b"
fi
# 输出:10 大于 5# 用 ((...)) 更简洁(支持 >、< 等符号)
if (( a > b )); thenecho "$a 大于 $b"
fi
# 输出:10 大于 5
3. 字符串运算符
用于字符串比较,语法:[ 字符串1 运算符 字符串2 ]
或 [[ 字符串1 运算符 字符串2 ]]
。
运算符 | 含义 | 示例(s1="abc", s2="abd") |
---|---|---|
= | 等于(== 等价) | [ "$s1" = "$s2" ] → false |
!= | 不等于 | [ "$s1" != "$s2" ] → true |
-z | 字符串长度为 0 | [ -z "$s1" ] → false |
-n | 字符串长度不为 0 | [ -n "$s1" ] → true |
< | 字典序小于(需 [[]]) | [[ "$s1" < "$s2" ]] → true(abc < abd) |
> | 字典序大于(需 [[]]) | [[ "$s1" > "$s2" ]] → false |
#!/bin/bashs1="abc"
s2="abd"# 字符串是否相等
if [ "$s1" = "$s2" ]; thenecho "相等"
elseecho "不相等"
fi
# 输出:不相等# 字符串长度是否不为0
if [ -n "$s1" ]; thenecho "$s1 长度不为0"
fi
# 输出:abc 长度不为0# 字典序比较(需用 [[ ]])
if [[ "$s1" < "$s2" ]]; thenecho "$s1 小于 $s2"
fi
# 输出:abc 小于 abd
4. 逻辑运算符
用于组合条件,支持与(-a
或 &&
)、或(-o
或 ||
)、非(!
)。
#!/bin/basha=10
b=5
c=15# 与运算(两个条件都成立)
if [ $a -gt $b ] && [ $a -lt $c ]; thenecho "$a 大于 $b 且小于 $c"
fi
# 输出:10 大于 5 且小于 15# 或运算(至少一个条件成立)
if [ $a -gt $c ] || [ $a -gt $b ]; thenecho "$a 大于 $c 或大于 $b"
fi
# 输出:10 大于 15 或大于 5# 非运算(条件取反)
if ! [ $a -eq $b ]; thenecho "$a 不等于 $b"
fi
# 输出:10 不等于 5
第二部分:核心语法
1. 条件判断 (if-elif-else
)
让脚本根据不同条件执行不同操作。
基本结构:
if [ condition ]; then# command to execute if condition is true elif [ another_condition ]; then# command to execute if another_condition is true else# command to execute if all conditions are false fi
关键点:
if
和[
之间必须有空格。[
和condition
之间以及condition
和]
之间也必须有空格。then
必须单独一行,或者用分号;
与if
语句放在同一行:if [ condition ]; then ...
推荐使用
[[ ... ]]
,它更强大且不易出错,尤其是在处理字符串时。
#!/bin/bashread -p "Enter a number: " NUMif [[ $NUM -gt 100 ]]; thenecho "$NUM is greater than 100." elif [[ $NUM -eq 100 ]]; thenecho "$NUM is exactly 100." elseecho "$NUM is less than 100." fi
字符串比较:
操作符
描述
==
等于
!=
不等于
-z
字符串为空
-n
字符串不为空
#!/bin/bashread -p "Enter 'yes' or 'no': " ANSWERif [[ $ANSWER == "yes" ]]; thenecho "You chose 'yes'." elif [[ $ANSWER != "no" ]]; thenecho "Invalid input." elseecho "You chose 'no'." fi
文件系统判断:
操作符
描述
-f
文件存在且是常规文件
-d
文件存在且是目录
-e
文件存在(不区分类型)
-r
文件可读
-w
文件可写
-x
文件可执行
#!/bin/bashFILE="./hello.sh"if [[ -f "$FILE" ]]; thenecho "$FILE is a regular file."if [[ -x "$FILE" ]]; thenecho "And it is executable."fi elif [[ -d "$FILE" ]]; thenecho "$FILE is a directory." elseecho "$FILE does not exist or is not a regular file/directory." fi
2. case
语句
当有多个分支选择时,case
是比 if-elif-else
更清晰的替代方案。
#!/bin/bashread -p "Enter a character (a, b, or c): " CHARcase $CHAR ina|A)echo "You entered 'a'.";;b|B)echo "You entered 'b'.";;c|C)echo "You entered 'c'.";;*)echo "Invalid character.";;
esac
关键点:
|
用于匹配多个模式。*)
是一个通配符,匹配任何其他输入。每个分支以
;;
结束。整个
case
语句以esac
(case反写) 结束。
3. 循环结构
for
循环:遍历列表:
#!/bin/bashfor FRUIT in apple banana orange; doecho "I like $FRUIT." done
遍历数字序列:
#!/bin/bashecho "Counting from 1 to 5:" for i in {1..5}; doecho $i done
C 语言风格的
for
循环:#!/bin/bashfor (( i=0; i<5; i++ )); doecho "C-style loop, iteration: $i" done
while
循环: 当条件为真时持续循环。常用于逐行读取文件。#!/bin/bashCOUNTER=0 while [[ $COUNTER -lt 5 ]]; doecho "Counter is $COUNTER"# 必须有改变条件的语句,否则会死循环let COUNTER++ done
逐行读取文件(重要用法):
#!/bin/bashFILENAME="hello.sh" while IFS= read -r line; doecho "Line: $line" done < "$FILENAME"
IFS= read -r line
是读取文件的标准、安全的方式,可以防止read
命令意外地处理反斜杠和行首行尾的空白字符。
until
循环: 当条件为假时持续循环,直到条件为真。#!/bin/bashCOUNTER=0 until [[ $COUNTER -ge 5 ]]; doecho "Counter is $COUNTER"let COUNTER++ done
4. 函数 (Functions)
将代码块封装成函数,方便复用。
定义和调用:
#!/bin/bash# 定义函数 greet() {echo "Hello there!" }# 调用函数 echo "Calling the function..." greet echo "Function called."
传递参数:
在函数内部,
$1
,$2
,$3
, ... 分别代表第一个、第二个、第三个参数。$@
代表所有参数的列表。$#
代表传递给函数的参数个数。
#!/bin/bashprint_info() {if [[ $# -eq 0 ]]; thenecho "Usage: print_info <name> <age>"return 1 # 返回一个非零值表示错误fiecho "Name: $1"echo "Age: $2"echo "All arguments: $@" }print_info "Bob" 42 print_info # 测试错误处理
返回值:
Shell 函数的
return
语句只返回一个 0-255 的整数,称为 退出状态码。0
通常表示成功,非0
表示失败。要 "返回" 数据,通常是在函数中用
echo
输出,然后在调用处用命令替换$(...)
来捕获输出。local
关键字使变量的作用域仅限于函数内部,这是一个好习惯。
#!/bin/bashget_full_name() {local first_name=$1local last_name=$2# 使用 echo "返回" 结果echo "$first_name $last_name" }# 使用命令替换捕获函数的输出 FULL_NAME=$(get_full_name "John" "Doe") echo "The full name is: $FULL_NAME"
第三部分:高级用法
1. 数组 (Arrays)
索引数组 (Indexed Arrays):
#!/bin/bash# 定义数组 fruits=("Apple" "Banana" "Cherry")# 访问元素(索引从0开始) echo "First fruit: ${fruits[0]}"# 访问所有元素 echo "All fruits: ${fruits[@]}"# 获取数组长度 echo "Number of fruits: ${#fruits[@]}"# 添加元素 fruits+=("Orange")# 遍历数组 for fruit in "${fruits[@]}"; doecho "Processing $fruit" done
关联数组 (Associative Arrays / Hashes): 键值对数组(需要 Bash 4.0+)。
#!/bin/bash# 声明一个关联数组 declare -A user# 赋值 user["name"]="Alice" user["id"]="101" user["email"]="alice@example.com"# 访问元素 echo "User Name: ${user[name]}"# 遍历所有的键 echo "All keys: ${!user[@]}"# 遍历所有的值 echo "All values: ${user[@]}"# 遍历键值对 for key in "${!user[@]}"; doecho "$key: ${user[$key]}" done
2. 字符串处理
操作 | 示例 | 结果 |
---|---|---|
长度 | ${#str} | 字符串长度 |
截取 | ${str:2:3} | 从索引 2 开始取 3 个字符 |
替换 | ${str/old/new} | 替换第一个 old 为 new |
全替换 | ${str//old/new} | 替换所有 old 为 new |
删除前缀 | ${str#prefix} | 删除最短前缀 |
删除后缀 | ${str%suffix} | 删除最短后缀 |
示例:
#!/bin/bash
str="hello world"
echo "长度:${#str}" # 输出:11
echo "截取:${str:3:4}" # 输出:lo w
echo "替换:${str/world/shell}" # 输出:hello shell
3. 精细数学计算
Shell 内置的 $((...))
只支持整数运算。对于浮点数或更复杂的计算,需要借助外部工具。
整数计算:
#!/bin/bashA=10 B=3SUM=$((A + B)) PRODUCT=$((A * B)) REMAINDER=$((A % B)) # 取余echo "Sum: $SUM, Product: $PRODUCT, Remainder: $REMAINDER"
浮点数计算 (
bc
):bc
是一个强大的计算器,-l
参数可以加载数学库,支持高精度计算。#!/bin/bash# 将表达式通过管道传给 bc RESULT=$(echo "scale=4; 10 / 3" | bc) echo "10 / 3 = $RESULT"# 更复杂的计算 PI=$(echo "scale=10; 4*a(1)" | bc -l) # a() 是反正切函数,4*a(1)是计算pi的经典方法 echo "Pi ≈ $PI"VAR1=5.5 VAR2=2.2 SUM=$(echo "$VAR1 + $VAR2" | bc) echo "$VAR1 + $VAR2 = $SUM"
使用
awk
计算:awk
也是一个处理文本和进行计算的强大工具。#!/bin/bashRESULT=$(awk "BEGIN {printf \"%.4f\", 10/3}") echo "10 / 3 = $RESULT"
4. 颜色和格式化输出
通过 ANSI 转义序列设置输出颜色,格式:\033[颜色代码m文本\033[0m
(0m
重置颜色)
常用颜色代码:
- 文本色:30(黑)、31(红)、32(绿)、33(黄)、34(蓝)、35(紫)、36(青)、37(白)
- 背景色:40(黑)、41(红)、42(绿)等
使用 ANSI escape codes 来控制终端输出的颜色和样式。
语法:
\e[...m
或\033[...m
示例:
#!/bin/bash# 为了可读性和复用,最好将颜色代码定义为变量 COLOR_RESET='\e[0m' COLOR_RED='\e[31m' COLOR_GREEN='\e[32m' COLOR_YELLOW='\e[33m' BG_BLUE='\e[44m' STYLE_BOLD='\e[1m'echo -e "${COLOR_RED}This is red text.${COLOR_RESET}" echo -e "${COLOR_GREEN}This is green text.${COLOR_RESET}" echo -e "${STYLE_BOLD}${COLOR_YELLOW}This is bold yellow text.${COLOR_RESET}" echo -e "${BG_BLUE}This text has a blue background.${COLOR_RESET}"# 组合使用 echo -e "${STYLE_BOLD}${COLOR_RED}${BG_BLUE}DANGER! Critical Error!${COLOR_RESET}"
echo -e
是必须的,它让echo
能够解释转义序列。
5. 强大的命令执行与控制
命令分组:
( ... )
: 在一个 子 Shell 中执行命令组。子 Shell 中的变量和目录改变不会影响父 Shell。{ ...; }
: 在 当前 Shell 中执行命令组。注意}
前必须有分号或换行。
# 子Shell示例 echo "Before: PWD=$PWD" (cd /tmp; echo "Inside subshell: PWD=$PWD") echo "After: PWD=$PWD" # PWD 没变# 当前Shell示例 echo "Before: PWD=$PWD" { cd /var; echo "Inside group: PWD=$PWD"; } echo "After: PWD=$PWD" # PWD 变了
输入/输出重定向:
>
: 重定向标准输出(会覆盖文件内容)。ls > file.txt
>>
: 重定向标准输出(追加到文件末尾)。date >> file.txt
<
: 重定向标准输入。read -r line < file.txt
2>
: 重定向标准错误。command_that_fails 2> error.log
&>
: 重定向标准输出和标准错误。command &> all_output.log
/dev/null
: 一个特殊的设备文件,所有写入它的数据都会被丢弃("黑洞")。常用于丢弃不想要的输出。command > /dev/null 2>&1
管道 (
|
): 将前一个命令的标准输出作为后一个命令的标准输入。这是 Shell 的精髓之一。# 统计当前目录有多少个 .sh 文件 ls -l | grep ".sh$" | wc -l
进程替换 (
<(command)
): 这是一个非常高级的特性,它将一个命令的输出伪装成一个文件,然后可以被另一个需要文件作为输入的命令使用。# 比较两个目录下的文件列表,而无需创建临时文件 diff <(ls /bin) <(ls /usr/bin)
Here 文档(
<<
)
向命令输入多行文本(无需手动输入),语法
命令 << 分界符多行文本
分界符
#!/bin/bash# 向文件写入多行内容
cat << EOF > info.txt
Name: Alice
Age: 25
City: Beijing
EOF
# info.txt 内容为上述三行# 作为函数输入
count_lines() {wc -l
}count_lines << EOF
line1
line2
line3
EOF
# 输出:3(行数)
第四部分:编写健壮的脚本
1. set
命令
在脚本开头使用 set
命令可以使其更安全、更健壮。
#!/bin/bash
set -euo pipefail
set -e
: 脚本中任何命令失败(返回非零退出状态码)时,立即退出。set -u
: 尝试使用未定义的变量时,立即退出。set -o pipefail
: 在管道中,只要有任何一个命令失败,整个管道的退出状态码就是失败的。
2. 解析脚本选项 (getopts
)
用于解析传递给脚本的命令行选项(如 -f
, -v
)。
#!/bin/bash
set -euo pipefailVERBOSE=false
FILENAME=""# f: 表示-f选项需要一个参数
# v 表示-v选项不需要参数
while getopts 'vf:' OPTION; docase "$OPTION" inv)VERBOSE=true;;f)FILENAME="$OPTARG";;?)echo "Usage: $(basename $0) [-v] [-f filename]"exit 1;;esac
doneif [[ $VERBOSE == true ]]; thenecho "Verbose mode is ON."
fiif [[ -n "$FILENAME" ]]; thenecho "Processing file: $FILENAME"
elseecho "No filename provided."
fi
执行示例:
./myscript.sh -v -f data.txt
./myscript.sh -f report.csv
3. 信号陷阱 (trap
)
允许你在脚本接收到特定信号(如 Ctrl+C
)时执行一段代码,常用于清理临时文件。
#!/bin/bash
set -euo pipefail# 创建一个临时文件
TMP_FILE=$(mktemp)
echo "Created temporary file: $TMP_FILE"# 定义清理函数
cleanup() {echo "Caught signal! Cleaning up..."rm -f "$TMP_FILE"echo "Cleanup finished."exit 1
}# 设置陷阱:当接收到 INT(Ctrl+C) 或 TERM 信号时,执行 cleanup 函数
trap cleanup INT TERM# 主逻辑
echo "Script is running, press Ctrl+C to test the trap."
sleep 60 # 模拟长时间运行的任务
echo "Script finished normally."# 正常退出前也要清理
rm -f "$TMP_FILE"
捕获系统信号(如 Ctrl+C
发送的 SIGINT
),执行自定义操作。
#!/bin/bash# 捕获 SIGINT 信号(Ctrl+C)
trap 'echo " 不要按 Ctrl+C!"; exit 1' SIGINTecho "运行中(按 Ctrl+C 测试)..."
while true; dosleep 1
done
4. 正则表达式与文本处理
Shell 结合 grep
(搜索)、sed
(编辑)、awk
(分析)可强大处理文本。
1. grep
:文本搜索
- 语法:
grep [选项] 模式 文件
- 常用选项:
-i
(忽略大小写)、-v
(反向匹配)、-n
(显示行号)、-E
(扩展正则)。
#!/bin/bash# 在文件中搜索包含 "error" 的行(区分大小写)
grep "error" log.txt# 忽略大小写搜索,显示行号
grep -in "warning" log.txt# 反向匹配(不包含 "debug" 的行)
grep -v "debug" log.txt# 扩展正则(-E),匹配 "apple" 或 "banana"
grep -E "apple|banana" fruits.txt
2. sed
:文本替换与编辑
- 语法:
sed [选项] '命令' 文件
- 常用命令:
s/原字符串/新字符串/
(替换,默认替换每行第一个匹配)、s/.../.../g
(全局替换)。
#!/bin/bash# 替换文件中 "old" 为 "new"(仅输出,不修改原文件)
sed 's/old/new/' text.txt# 全局替换并修改原文件(-i 选项,备份用 -i.bak)
sed -i 's/hello/HELLO/g' greet.txt # 所有 hello 替换为 HELLO# 删除空行(d 命令删除匹配行)
sed '/^$/d' input.txt # ^$ 匹配空行
3. awk
:文本分析与处理
擅长按列处理文本(默认空格分隔),语法:awk '模式 {动作}' 文件
。
#!/bin/bash# 打印文件第2列和第4列
awk '{print $2, $4}' data.txt# 按条件过滤(第3列数值 > 100 的行)
awk '$3 > 100 {print $0}' data.txt # $0 表示整行# 自定义分隔符(-F 选项),按逗号分隔,打印第1列
awk -F ',' '{print $1}' csvfile.txt
5. 进程管理
Shell 可启动、查看、终止进程。
1. 后台运行与 jobs
&
:在命令后加&
使其后台运行。jobs
:查看当前终端的后台进程。fg %n
:将第 n 个后台进程调回前台。bg %n
:将暂停的后台进程继续运行。
#!/bin/bash# 后台运行长时间任务(输出重定向到文件)
sleep 30 > sleep.log 2>&1 &
echo "后台进程 ID:$!" # $! 是后台进程 PID# 查看后台进程
jobs# 终止进程(kill)
pid=$!
kill $pid # 发送 SIGTERM 信号
# kill -9 $pid # 强制终止(SIGKILL,无法捕获)
2. 进程替换(<(命令)
、>(命令)
)
将命令输出作为临时文件,用于需要文件参数的命令。
#!/bin/bash# 比较两个命令的输出(无需临时文件)
diff <(ls dir1) <(ls dir2) # 比较 dir1 和 dir2 的文件列表# 将输出同时发送到终端和文件(tee 命令)
ls -l | tee >(grep ".sh" > sh_files.txt) # 筛选 .sh 文件到 sh_files.txt,同时显示所有
6. 调试脚本
bash -n 脚本
:检查语法错误(不执行)。bash -x 脚本
:执行并输出每一行命令(调试细节)。- 在脚本中用
set -x
(开启调试)、set +x
(关闭)。
#!/bin/bashset -x # 开启调试
a=5
b=3
echo $((a + b))
set +x # 关闭调试
echo "调试结束"
第五部分:综合脚本示例
案例 1:批量重命名文件
#!/bin/bash
# 功能:将当前目录所有 .txt 文件重命名为 "prefix_数字.txt"(如 prefix_1.txt)count=1
for file in *.txt; do# 跳过非文件(如目录)if [ ! -f "$file" ]; thencontinuefi# 重命名mv "$file" "prefix_${count}.txt"echo "重命名:$file → prefix_${count}.txt"((count++))
done
案例 2:系统监控脚本
#!/bin/bash
# 功能:监控系统 CPU、内存、磁盘使用率,超过阈值则报警# 阈值(百分比)
CPU_THRESHOLD=80
MEM_THRESHOLD=80
DISK_THRESHOLD=90# 获取 CPU 使用率(取整数)
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}' | cut -d. -f1)# 获取内存使用率
mem_usage=$(free | grep Mem | awk '{print $3/$2 * 100}' | cut -d. -f1)# 获取磁盘使用率(根目录)
disk_usage=$(df -h / | grep / | awk '{print $5}' | sed 's/%//')# 检查 CPU
if (( cpu_usage > CPU_THRESHOLD )); thenecho "报警:CPU 使用率过高($cpu_usage%)"
fi# 检查内存
if (( mem_usage > MEM_THRESHOLD )); thenecho "报警:内存使用率过高($mem_usage%)"
fi# 检查磁盘
if (( disk_usage > DISK_THRESHOLD )); thenecho "报警:磁盘使用率过高($disk_usage%)"
fi
总结
掌握 Shell 脚本是一个循序渐进的过程。
入门: 从
echo
、变量、read
和简单的if
开始,学会编写和执行基础脚本。进阶: 熟练运用循环、函数、
case
语句和各种条件判断,能够编写逻辑复杂的脚本。高级: 掌握数组、
bc
计算、颜色输出、进程替换、getopts
和trap
等高级特性,编写出功能强大、交互友好且非常健壮的专业脚本。
最重要的是 多写、多练、多看。尝试用脚本去自动化你日常工作中的重复性任务,这是最好的学习方式。