语法检查
在 shell 脚本加这一行,代表语法检查,不会运行脚本
#!/bash/bin
set -o noexec
通过命令行语法检查
bash -n ./xxx.sh
shellcheck 网站检查ShellCheck – shell script analysis tool
调试选项
#!/bash/bin
set -o verbose # 在命令私信前输出命令行的内容
set -o xtrace # 输出命令行展开后的内容
set -o nounset # 使用未定义的变量时返回错误
set -o errexit # 当命令退出时的状态码为非0时,立即停止shell运行
可选择使用,也可部分开启。例如
#!/bash/bin
set -o xtrace # 开启
ls
set +o xtrace # 关闭
xzca
if 参数
计算数值合计值
# 语法 # sum.sh NUMBER... # # 说明 # 将参数中指定的所有数值的合计值输出到标准输出 # 只能指定整数数值(包括 0 以及负数),不能指定小数
#!/bin/bash
# 取出该脚本文件名,##*/ 代表取路径中最后一个 / 后面的内容
readonly SCRIPT_NAME=${0##*/}
result=0
for number in "$@"
do
# ! 代表不等于, =~ 代表右边的扩展为正则表达,连续的数值或带负号的连续数值
if [[ ! $number =~ ^-?[0-9]+$ ]]; then
# 1>&2 将错误输出到标准输出,即屏幕终端
printf '%s\n' "${SCRIPT_NAME}: '$number': non-integer number" 1>&2
exit 1
fi
((result+=number)) # 累加
done
printf '%s\n' "$result"
计算数值列表的合计值
从标准输入读取数值,并将合计值输出到标准输出,代码与上方一样,循环主题不一样
#!/bin/bash
readonly SCRIPT_NAME=${0##*/}
result=0
# read 命令从标准输入逐行读取数据,保存到变量 number 中,-r 逐行读取参数
while IFS= read -r number
do
if [[ ! $number =~ ^-?[0-9]+$ ]]; then
printf '%s\n' "${SCRIPT_NAME}: '$number': non-integer number" 1>&2
exit 1
fi
((result+=number))
done
printf '%s\n' "$result"
计算某列的合计值
利用上面的脚本计算 ls -l 显示的文件大小
ls -l | tail -n +2 | sed 's/ \{2,\}/ /g' | cut -d ' ' -f 5 | ./sum.sh
-n +2 从第二行开始输入,不要第一行的 total 4
sed 's/ \{2,\}/ /g' 连续的空格替换为 1 个
cut -d ' ' -f 5 取出第五列
输出指定用户信息
从 /etc/passwd 中取出指定用户信息
#!/bin/bash
readonly SCRIPT_NAME=${0##*/}
user=$1
# -z 是否为空字符串
if [[ -z $user ]]; then
printf '%s\n' "${SCRIPT_NAME}: missing username" 1>&2
exit 1
fi
cat /etc/passwd \
| grep "^${user}:" \
| {
# : read 会使用冒号分割读取行内容,从头以此赋值给变量
IFS=: read -r username password userid groupid \
comment homedirectory loginshell
# $? -ne 0 如果 grep 输出为空
if [[ $? -ne 0 ]]; then
printf '%s\n' "${SCRIPT_NAME}: '$user': No such user" 1>&2
exit 2
fi
cat <<END # 输出以下多行
username = $username
userid = $userid
groupid = $groupid
homedirectory = $homedirectory
loginshell = $loginshell
END
}
对正则表达式中的元字符进行转义输出
.*+?\^$(){|[ 12 个表达式
printf '%s\n' "$1" \
| sed 's/[.*+?\^$(){|[]/\\&/g'
根据文件格式解压缩
#!/bin/bash
# 文件名
# extract.sh - 解压文件
#
# 语法
# extract.sh FILE...
#
# 说明
# 解压指定的压缩文件
# 可以处理的压缩文件的格式如下
# .gz
# .bz2
# .xz
# .tar
# .tar.gz, .tgz
# .tar.bz2, .tbz2
# .tar.xz, .txz
# .zip
readonly SCRIPT_NAME=${0##*/}
extract_one()
{
local file=$1
# -z 是否为空
if [[ -z $file ]]; then
printf '%s\n' "${SCRIPT_NAME}: missing file operand" 1>&2
return 1
fi
# ! -f 是否存在
if [[ ! -f $file ]]; then
printf '%s\n' "${SCRIPT_NAME}: '$file': No such file" 1>&2
return 2
fi
# 删除扩展名如 test.zip ,base 得到的值是 test
local base="${file%.*}"
case "$file" in
*.tar.gz | *.tgz)
tar xzf "$file"
;;
*.tar.bz2 | *.tbz2)
tar xjf "$file"
;;
*.tar.xz | *.txz)
tar xJf "$file"
;;
*.tar)
tar xf "$file"
;;
*.gz)
gzip -dc -- "$file" > "$base" # -- 代表文件名用原来的,不做特殊含义
;;
*.bz2)
bzip2 -dc -- "$file" > "$base"
;;
*.xz)
xz -dc -- "$file" > "$base"
;;
*.zip)
unzip -q -- "$file"
;;
*) # 不满足前面任何一条件
printf '%s\n' "${SCRIPT_NAME}: '$file': unexpected file type" 1>&2
return 3
;;
esac
}
# 输入的参数个数,-le 小于等于 0
if [[ $# -le 0 ]]; then
printf '%s\n' "${SCRIPT_NAME}: missing file operand" 1>&2
exit 1
fi
result=0
for i in "$@"
do
extract_one "$i" || result=$? # 调用函数返回值,非 0 才会赋值,代表有失败的
done
exit "$result"
相对路径转为绝对路径
#!/bin/bash
# 文件名
# absolutepath.sh - 将相对路径转换为绝对路径
#
# 语法
# absolutepath.sh FILE
#
# 说明
# 将指定的相对路径转换为绝对路径,并将结果输出到标准输出
readonly SCRIPT_NAME=${0##*/}
path=$1
if [[ -z $path ]]; then
printf '%s\n' "${SCRIPT_NAME}: missing file operand" 1>&2
exit 1
fi
# -d 路径存在且为目录
if [[ -d $path && $path != */ ]]; then
path+=/
fi
dir=
file=
# =~ 正则表达式,如目录为 ./123/wqe/ee.txt 第一个括号为 ./123/wqe,第二个括号为 ee.txt
# BASH_REMATCH 为 bash 中创建的数组,第一个括号匹配到的索引为 1 保存,以此类推
if [[ $path =~ ^(.*/)(.*)$ ]]; then
dir=${BASH_REMATCH[1]}
file=${BASH_REMATCH[2]}
else # 如果匹配不到,则判断就在当前目录,直接输出
printf '%s\n' "${PWD}/${path}"
exit 0
fi
# ! -d 判断目录不存在
if [[ ! -d $dir ]]; then
printf '%s\n' "${SCRIPT_NAME}: '$dir': No such directory" 1>&2
exit 2
fi
# 进入到该目录,执行 pwd 输出改目录的绝对路径
basedir=$(cd -- "$dir" && pwd)
if [[ $basedir == / ]]; then # 就在当前目录的情况
basedir=
fi
printf '%s\n' "${basedir}/${file}"
在多个文件中查找字符串
两个参数,匹配字符串,目录,在指定目录中递归遍历查找所有文件,第二个参数不写,默认当前目录
#!/bin/bash
# 文件名
# findgrep.sh - 按照指定模式对目录下的文件进行匹配,并输出匹配到的行
#
# 语法
# findgrep.sh PATTERN [DIRECTORY]
#
# 说明
# 递归查找指定目录下的文件,并输出文件中与指定模式相匹配的行
# 匹配模式按照基本正则表达式处理
# 如果没有指定要查找的目录,则把当前目录当作查找目录
readonly SCRIPT_NAME=${0##*/}
pattern=$1
directory=$2
# -z 字符串为空
if [[ -z $pattern ]]; then
printf '%s\n' "${SCRIPT_NAME}: missing search pattern" 1>&2
exit 1
fi
if [[ -z $directory ]]; then
directory=. # . 当前目录
fi
# ! -d 不存在目录
if [[ ! -d $directory ]]; then
printf '%s\n' "${SCRIPT_NAME}: '$directory': No such directory" 1>&2
exit 2
fi
# -- 后面的目录为原样,不做特殊含义
# -type f 查找类型为普通文件,d 表示目录,l 表示符号链接
# -print0 查找结果输出到标准输出,多个结果以 null 区分
# xargs 从标准输入接收数据,将数据作为参数传递给要执行的命令
# /dev/null 至少给 grep 提供一个输入文件,对结果没有啥影响
find -- "$directory" -type f -print0 | xargs -0 grep -e "$pattern" -- /dev/null