Unix Shell Programming - tenji/ks GitHub Wiki

Unix Shell 编程

一、注意事项

1.1 在调用之前声明

必须在调用函数地方之前,声明函数,shell 脚本是逐行运行。不会像其它语言一样先预编译。一次必须在使用函数前先声明函数。

1.2 shell 脚本使用的是非交互式方式

在非交互式模式下alias扩展功能默认是关闭的,此时虽然可以定义alias别名,但是 shell 不会将alias别名扩展成对应的命令,而是将alias别名本身当作命令执行,如果 shell 内置命令和 PATH 中均没有与alias别名同名的命令,则 shell 会找不到指定的命令。

1.3 命令的执行状态

命令执行状态只有0为真,其它均为假,也就是说可以命令执行结果是否为0来判断命令是否执行成功。比如:

#!/bin/bash -l

grep "test" /home/admin/web.xml > /dev/null
if [ $? -eq 0 ]; then
    echo "Found!"

以上脚本可以判断文件web.xml中是否包含字符串test,但是需要注意的是,如果脚本中设置了set -e,命令执行状态不为真,也就是不为0的时候,会导致脚本出错自动退出。

1.4 shell 循环中变量的作用域问题

shell 中使用管道会生成一个子 shell,在子 shell 中使用 while, for 循环的代码也是在子 shell 中执行的,所以在循环中的修改的变量只在子 shell 中有效,当循环结束时,会回到主 shell,子 shell 中修改的变量不会影响主 shell 中的变量。

A="1"
B="2"
C="/home/linux/a"
 
cat $C | grep -v '^commit' | while read line
do
     if [ "x$A" = "x1" ]; then
         B=$A
         echo $B
     fi
done
 
echo $B
 
# 第一个echo打印的是1
# 第二个echo打印的是2

这里是因为在子 shell 中的 while 循环中的 B 只是主 shell 中 B 的一个副本,在子 shell 中对 B 重新赋值是不能影响到父 shell 的,所以最后 echo $B 时值没有改变。但是以下是可以重新赋值的,因为这里没有管道,也就不存在子 shell 了。

while read line
do
   if
   B=$A
   fi
done < $C

1.5 shell 脚本调试

  • -n

    读一遍脚本中的命令但不执行,用于检查脚本中的语法错误。

  • -v

    一边执行脚本,一边将执行过的脚本命令打印到标准错误输出。

  • -x

    提供跟踪执行信息,将执行的每一条命令和结果依次打印出来。

1.6 文本处理三剑客

  • grep (Global Regular Expression Print)

    grep 命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来,擅长行处理和查找

  • awk (Aho Weinberger Kernaighan)

    AWK 是一种处理文本文件的语言,它将文件作为记录序列处理,每行内容都会被分割成一系列的域,因此,我们可以认为一行的第一个词为第一个域,第二个词为第二个,以此类推,擅长列处理

  • sed (stream editor)

    sed是一种流编辑器,它一次处理一行内容,擅长行处理和替换

二、特殊变量

名称 说明
$0 脚本名称
$1-9 脚本执行时的参数1到参数9
$? 脚本的返回值
$# 脚本执行时,输入的参数的个数
$@ 输入的参数的具体内容
$* 输入的参数的具体内容

$@$*的区别:

  • $@将输入的参数作为一个列表对象
  • $*将输入的参数作为一个单词

三、常用场景

  • 判断命令是否存在
#!/bin/bash

if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  exit 1
fi
  • 判断命令是否执行成功
#!/bin/bash

if [ $? -eq 0 ]; then
  命令正确的分支
else
  命令失败的分支
fi
  • 使用 getopts 来读取参数
#!/bin/bash

while getopts "a:bc" arg #选项后面的冒号表示该选项需要参数
do
    case $arg in
         a)
            echo "a's arg:$OPTARG" #参数存在$OPTARG中
            ;;
         b)
            echo "b"
            ;;
         c)
            echo "c"
            ;;
         ?)  #当有不认识的选项的时候arg为?
        echo "unkonw argument"
    exit 1
    ;;
    esac
done

可以结合一下语句来判断参数:

if [ ! -n "$1" ] ;then
    echo "Usage: install.sh -p [LOGSTASH_PATH]"
    exit 1
fi
  • 脚本出错自动退出运行
#!/bin/bash

set -e
  • 读取JSON格式数据(日志文件等)中的某个字段
#!/bin/bash

grep -Po 'receive_time[" :]+\K[^,"]+' android_log.json |awk '$1>=1558944000000 && $1 <=1558947600000' |wc -l
  • 判断字符是否符合指定正则表达式
#!/bin/bash

if ! [[ $target_release_version =~ ^[0-9]{1}\.[0-9]{1}\.[0-9]{1}$ ]]; then
  echo 'Unavailable release version, use version like 2.0.0' >&2
  exit 1
fi
  • 读取XML文件的属性值 比如读取一下的version值:
<version>0.0.1-SNAPSHOT</version>
#!/bin/bash

version=`awk '/<version>/,/<version>/ {print $0}' pom.xml | awk -v FS="<version>" -v OFS=" " '{print $2}' | awk -v FS="</version>" -v OFS=" " '{print $1}' | head -n 1`
echo "The version is: $version"
  • 读取文件名
#!/bin/bash

filename=`ls -l | awk '{print $9}'`
echo $filename
  • 逐行读取文件
#!/bin/bash

cat partitions.log | while read line
do
    echo $line
done
⚠️ **GitHub.com Fallback** ⚠️