返回

Shell编程基础

变量

脚本开头使用#!/bin/bash指定要使用的shell,shell会通过PATH环境变量来查找脚本中使用的命令,可以使用echo $PATH来看下脚本会在哪些目录下查找命令,如果脚本中执行的命令没有在PATH环境变量中,也可以通过绝对或相对路径来引用. 在脚本中可以使用shell维护的环境变量也可以定义和使用自己的用户变量. 赋值等号两边不能存在空格

  • 环境变量: 可以使用set命令查看当前环境变量列表,例如$HOME、$HOSTNAME、$USER等;

  • 用户变量: 由字母数字下划线组成,长度不超过二十,区分大小写,变量、等号、值之间不能有空格,在shell脚本结束时会被删除掉,使用美元符号引用,例如name=“test”。使用用户变量保存命令执行结果:testing=$(date)

  • 特殊参数变量

    参数 描述
    $ 0 $1 $2 入参
    $# 参数个数
    $* 入参列表,将所有参数当作单个参数
    $@ 入参列表,单独处理每个参

重定向输入输出

  • 输出重定向: > 追加:»
  • 输入重定向: <
  • 在脚本中使用< > 需要进行转义< >
  • 内联输入重定向:
command << marker
 content
marker
# 例如:
wc -l <<EOF
echo "aaa"
echo "BBB"
EOF

内联重定向到文件

cat >> demo.txt <<EOF
 some content
EOF
文件描述符 缩写 描述
0 STDIN 标准输入
1 STDOUT 标准输出
2 STDERR 标准错误
  • &> 符号会将所有输出重定向到一个文件内
  • exec 1>testout 在脚本中将STDOUT永久重定向到testout
  • exec 3>./testfile 创建写描述符3
  • exec 3>&- 关闭文件描述符3
  • cat /dev/null > file.log 可快速清空日志

脚本中执行数学运算

  1. 使用美元符和方括号,只支持整型计算
  • var1=$[1 + 5]
  • var2=$[$var1 * 2]
  • var3=$[2 / 3]
  1. 通过bc进行浮点型计算:
 var1=$(echo "scale=4; 3.44 / 5" | bc)
 var1=10.46
 var2=43.67
 var3=33.2
 var4=71
 var5=$(bc<<EOF
 scale = 4
 a1=($var1 * $var2)
 b1=($var3 * $var4)
 a1+b1
 EOF
 )
 echo "$var5"

退出脚本

默认情况shell会以脚本最后一个命令的退出状态码退出(0-255)

可以使用exit命令指定脚本的退出码,并通过echo $?查看程序是否执行成功

结构化命令

if语句

根据命令执行是否成功走不同分支

if command1; then
 comands
elif comand2; then
 comands
else
 comands
fi

或者使用条件测试的方式

if [ condition ];then
 comands
fi

条件测试

  1. 数值比较

    比较 描述
    n1 -eq n2 检查相等
    n1 -ge n2 检查n1是否大于或等于n2
    n1 -gt n2 检查n1是否大于n2
    n1 -le n2 检查n1是否小于或等于n2
    n1 -lt n2 检查n1是否小于n2
    n1 -ne n2 检查n1是否不等于n2
  2. 字符串比较

    比较 描述
    str1 = str2 是否相同
    str1 != str2 是否不同
    str1 < str2 str1是否比str2大
    str1 > str2 str1是否比str2小
    -n str1 检查str1的长度是否非0
    -z str1 检查str1的长度是否为0
  3. 文件比较

    比较 描述
    -d file file是否存在并是目录
    -e file file是否存在
    -f file file是否存在并是文件
    -r file 是否存在并可读
    -s file 是否存在并非空
    -w file 是否存在并可写
    -x file 是否存在并可执行
    -O file 是否存在并属于当前用户所有
    -G file 是否存在并默认组与当前用户相同
    file1 -nt file2 file1是否比file2新
    file1 -ot file2 file1是否比file2旧
  4. 复合条件测试

[ conditon1 ] && [ condition2 ] [ conditon1 ] || [ condition2 ]

  1. 使用双括号进行数学运算判断

(( expression )),支持的运算符号除普通运算符外还包括如下,大于小于符号不用转义 .

val++ 、val–、 ++val、 –val :自增自减

! :求反 ~:位求反 **:幂运算 «:左位移 »:右位移 &:位布尔和|:位布尔或 &&:逻辑和 ||:逻辑或

可以在if语句中使用双括号命令,也可以在脚本中的普通命令里使用来赋值

  1. 使用双方括号进行字符串比较

[[ express ]] bash shell支持模式匹配: if [[ $USER == r* ]]

case语句

case variable in
pattern1 | pattern2) commands1;;
pattern3) comands2;;
*) default commands;;
esac

for语句

for var in list
do
 commands:$var
done

有时候使用for line in $(cat $file)读取文本文件,默认会以空格分隔而不是换行符,需要修改IFS. IFS值:内部字段分隔符

IFS.OLD=$IFS
IFS=$’\n#<在代码中使用新的IFS值>
IFS=$IFS.OLD

C语言风格for语句

for  (( i=1; i <=10; i++ ))
do
...
done

while语句

while [ condition ]
do
 other commands
done

break: 跳出内部循环

continue: 跳过本次循环

break n: 可跳出外部循环

continue n : 继续循环层级n的循环

处理用户输入

利用getopts处理用户输入参数

while getopts :ab:c opt
do
 case "$opt" in
      a) echo "found -a option" ;;
      b) echo "found -b option ,with value $OPTARG" ;;
      c) echo "found -c option" ;;
      *) echo "Unknown option: $opt" ;;
      esac
done
shift $[ $OPTIND - 1 ]
count=1
for param in "$@"
do
    echo "Parameter $count: $param"
    count=$[ $count + 1]
done

获取用户输入

if read -t 10 -p "Please enter your name: " name
then
 echo "ok: $name"
else
 echo "too slow"
 exit 1
fi

使用临时文件

tempfile=$(mktemp tmp.XXXXXX)
exec 3>$tempfile
echo “test” >&3
exec 3>&-
rm -f $tempfile 2>/dev/null
mktemp -t 生成在/tmp/目录下,返回全路径

控制脚本

  1. 记录消息 date | tee -a testfile

  2. trap捕获处理信号

trap commands signals

trap “echo ‘ sorry i have trapped Ctrl-C’” SIGINT

trap – SIGINT 删除捕获

  1. 后台作业相关
  • command &:后台运行
  • nohup comand: 后台运行
  • jobs: 列举作业
  • bg: 后台重启作业
  • fg: 将后台作业调到前台继续执行,可指定job号
  • nice renice: 调整作业优先级
  • at: 定时执行作业
  • atq: 定时作业队列
  • atrm: 删除作业
  • crontab定时任务时间表:
  • min hour dayofmonth month dayofweek command
  • anacron:开机执行错过作业

shell函数

  • 函数名必须唯一,否则会有问题
function name{
 commands
}
[[或者]]
name() {
 commands
}
  • return 可以返回函数值:必须函数一结束就取$?;退出的状态码必须是0-255

  • 超出255值可以使用 result=$(func1) 方式

  • 每个函数可以单独使用自己的$# $1 $2 环境参数

  • 变量作用域:

  • 全局变量:不管在函数内外定义,脚本所有位置都可访问

  • 局部变量:必须用local关键字显式声明

local temp=$[ $value + 5]

数组相关: 函数参数是数组情况处理:

function testit {
 local newarray
 newarray=('echo "$@"')
 echo “The new array value is: ${newarray[*]}}
myarray=(1 2 3 4 5)
testit ${myarray[*]}

多进程执行函数

function multi {}
multi argu1&
multi argu2&
multi argu3&
wait

trap捕获信号

Trapping ctrl-c in Bash

You can use the trap builtin to handle a user pressing ctrl-c during the execution of a Bash script. e.g. if you need to perform some cleanup functions.

#!/bin/bash

[[trap]] ctrl-c and call ctrl_c()
trap ctrl_c INT

function ctrl_c() {
        echo "** Trapped CTRL-C"
}

for i in `seq 1 5`; do
    sleep 1
    echo -n "."
done

debug

  1. 在调用脚本的时候开启deubg sh -x shell.sh
  2. 在脚本文件首行开启deubg #!/bin/bash -x
  3. 使用set开启deubg set -x
  4. -v 显示脚本所有行,详细模式,在脚本嵌套调用时比较有用 sh -v shell.sh
  5. -n 检查脚本的语法,不执行脚本的命令 sh -n shell.sh

远程执行命令

ssh -n ‘command’ 可避免在while循环中吞stdin数据

工具推荐

shtool ShellCheck