SHELL 脚本
shell script 缺点:速度较慢,且使用的 CPU 资源较多,造成主机资源分配不良。
一个简单的 shell 脚本
模板
[why@yingzai shell]$cat sample.sh
#!/bin/bash
# 脚本名称: sample.sh
# 功能描述: 这是一个示例脚本,演示基本结构和常用功能
# 创建日期: 2025-03-21
# 作者: Your Name
# --------------------------
# 初始化配置
# --------------------------
SCRIPT_NAME=$(basename "$0") # 获取脚本名称
LOG_FILE="/tmp/${SCRIPT_NAME%.*}.log" # 生成日志文件名
TIMESTAMP=$(date +"%F %T") # 时间戳格式
# --------------------------
# 函数定义
# --------------------------
# 日志记录函数
log() {
echo "[${TIMESTAMP}] $1" | tee -a "$LOG_FILE"
}
# 错误处理函数
error_exit() {
log "错误: $1"
exit 1
}
# 帮助信息
usage() {
cat << EOF
用法:
$SCRIPT_NAME [选项] <参数>
选项:
-h, --help 显示帮助信息
-v, --version 显示版本信息
示例:
$SCRIPT_NAME input.txt
EOF
}
# --------------------------
# 主程序逻辑
# --------------------------
main() {
# 检查参数
if [[ $# -eq 0 ]]; then
usage
error_exit "未提供必要参数"
fi
# 处理输入文件
local input_file=$1
if [[ ! -f "$input_file" ]]; then
error_exit "文件不存在: $input_file"
fi
log "开始处理文件: $input_file"
# 示例处理流程
grep "error" "$input_file" /tmp/errors.txt && {
log "找到 $(wc -l < /tmp/errors.txt) 个错误"
} || {
log "未发现错误"
}
log "处理完成"
}
# --------------------------
# 脚本入口
# --------------------------
# 解析参数
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
-v|--version)
echo "版本 1.0"
exit 0
;;
*)
main "$1"
shift
;;
esac
done
说明
-
#!/bin/bash:解释器,告诉系统,该程序需要什么 shell 来执行
-
注释:#
-
环境变量的声明:PATH 和 LANG 是重要的环境变量
为了方便在脚本中执行外部命令,而不必写绝对路径,可以在脚本开头声明。
-
权限:shell 脚本执行需要 x 权限,
chmod a+x filename -
执行结果:可以自定义错误信息,将错误编码返回给系统
exit n
执行
执行方式
-
bash xxx.sh or sh xxx.sh or ./xxx.sh
- 在子进程中运行,不会影响父进程的环境变量
-
source xxx.sh or . xxx.sh (. 是 source 的简写)
-
在父进程中执行,影响父进程的环境变量
-
最典型的例子:
source /etc/bashrc
-
注意事项
-
命令执行是从上而下、从左而右
-
命令和参数间的多个空白会被忽略掉。tab 所得的空白会被视为空格。
-
空白行也会被忽略
-
读到 <Enter符号,就开始执行命令;换行可以使用 <Enter>
编写
与用户交互
-
语法
read -p "提示语" 变量名 -
示例
#!/bin/bash read -p "Please input you first name: " firstname read -p "Please input you last name: " lastname echo -e "\n you name: $firstname $lastname"
获取命令的执行结果
-
语法
变量名=$(命令) or 变量名=`命令` -
示例
#!/bin/bash date1=$(date +"%F %T") echo -e "now: $date1"
计算式
-
语法
变量=$(( 计算式)) -
示例
#!/bin/bash read -p "input you first number: " no_one read -p "input you second number: " no_second total=$(($no_one+$no_second)) echo -e "\ntotal: $total"
判断式
-
test 命令
-
文件类型

-
文件权限

-
文件比较

-
整数比较

-
字符串比较

-
多充条件判定

-
-
判断符号 [ ]
-
注意
-
中括号中的每个组件都需要用空格来分隔
-
中括号内的变量,最好以双引号扩起来
-
中括号内的常量,最好以单引号或双引号扩起来
-
-
示例
[why@yingzai shell]$name="ying zai" [why@yingzai shell]$[ $name == "ying" ] -bash: [: too many arguments [why@yingzai shell]$[ "$name" == "ying" ]
-
变量
-
默认变量
-
$0:脚本名
-
$1:第 1 个传入参数
-
$2:第 2 个传入参数
-
$n:第 n 个传入参数
-
$#:传入的参数个数
-
$@:表示所有参数
-
$*:表示 “$1c$2c$3c$4”,其中 c 为分隔字符,默认是空格。
不加双引号时,和
$@相同。加了双引号,会把参数视为一个参数$1$2$3$4。 -
$?:获取上一条命令的返回值。0 表示成功。
-
:获取脚本的 pid,把进程写进 pid 文件,方便管理。 ```shell echo $$ /tmp/nginx.pid ```
-
$_:获取命令最后一个参数
-
-
偏移变量:shift
-
示例
#!/bin/bash echo "file name: $0" echo "params number: $#" echo "params: $@" shift echo "params: $@" shift echo "params: $@" shift echo "params: $@"结果
[why@yingzai shell]$sh shift.sh 1 2 3 4 5 6 file name: shift.sh params number: 6 params: 1 2 3 4 5 6 params: 2 3 4 5 6 params: 3 4 5 6 params: 4 5 6
-
条件判断式
-
if 判断
-
if … then
if [ "$yn" == "YES" ] || [ "$yn" == "yes" ]; then xxx fi -
if … then … elif … then
if [ "$yn" == "YES" ] || [ "$yn" == "yes" ]; then xxx elif [ "$yn" == "NO" ] || [ "$yn" == "no" ]; then xxx fi -
if … then … elif … then … else
if [ "$yn" == "YES" ] || [ "$yn" == "yes" ]; then xxx elif [ "$yn" == "NO" ] || [ "$yn" == "no" ]; then xxx else xxx fi
-
-
case 判断
-
case … esac
#!/bin/bash read -p "Please input a name: " name case $name in "jim") echo "Hi, jim" ;; "tom") echo "Hello, tom" ;; *) echo "Hello, $name" ;; esac
-
循环
-
不定循环
-
while:满足条件,进入循环
while [ 条件 ] do xxx done -
until:满足条件,终止循环
until [ 条件 ] do xxx done
-
-
固定循环
-
for var in … do … done
#!/bin/bash for nu in $(seq 1 5) do echo -e "$nu" done -
for (( 初始值; 限制值; 步长 )) … do … done
#!/bin/bash read -p "Pleae input a number: " nu for (( i = 0; i < $nu; i++ )) do echo -e "i = $i" done
-
-
内置命令
-
exit:结束
-
break:跳出循环
-
continue:继续下次循环
-
return:返回一个值
-
-
扩展
-
指定分隔符
for 循环中默认以空格作为分隔符,所以获取变量时可能会导致错误。
解决方法:在 shell 脚本中指定 IFS 分隔符。[root@yingzai scripts]# vim test.sh #!/bin/bash a="12 3 4" b="5 6" c=($a $b) IFS=$'' #for i in ${c[@]} for i in {$a,$b} do echo $i done -
while 按行读取文件
#!/bin/bash # 写到标准输出 cat << EOF target: read lines from another file. auther: yingzai EOF while read line do col1=`echo $line|awk '{print $1}'` col2=`echo $line|awk '{print $2}'` echo -e "$col1 + $col2" done < error.log -
静默设置密码
[root@yingzai ~]#useradd user1 [root@yingzai ~]#echo "12345678" | passwd --stdin user1 Changing password for user user1. passwd: all authentication tokens updated successfully.
-
函数
-
语法
# 函数名后面有无括号都对 # 函数要写在主程序的前面,否则会调用失败,这是 shell script 的执行顺序导致的。 function 函数名 { xxx }
追踪和调试
选项
sh -nvx xxx.sh
-n: 不执行脚本,仅查询语法
-v: 在执行前,输出脚本内容
-x: 输出执行的脚本内容
Windows 和 Unix 断行符问题
Windows 断行符和 Unix 断行符替换,可以使用工具 dos2unix 和 unix2dos2。
dnf install dos2unix -y
dos2unix filename