shell 脚本案例

语法检查

在 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值