linux shell 编程

接收用户参数

#!/bin/bash
echo "当前脚本名称为$0"
echo "总共有$#个参数,分别是$*"
echo "第 1 个参数为$1,第 5 个为$5"

$# 参数个数;$* 所有位置参数;$? 上一次命令执行返回值;$1 第一个位置参数

执行 sh example.sh one two three four five six, 输出:

当前脚本名称为 example.sh
总共有 6 个参数,分别是 one two three four five six
第 1 个参数为 one,第 5 个为 five

判断用户参数

Shell 脚本中的条件测试语法可以判断表达式是否成立,若条件成立则返回数字 0,否则便返回其他随机数值。例如,在创建文件之前,首先判断文件是否存在。

语法:[ Expression ] 注:括号两侧有空格

区别 shell 命令的返回值与输出值,返回值通过 $? 获取,而 var=echo hhh 是将输出结果赋值给 var 而不是返回值,管道命令也是如此。

条件测试语句分为

  • 文件测试语句 (文件是否存在,权限是否满足等)
  • 逻辑测试
  • 整数比较
  • 字符串比较

在这里插入图片描述

例如,终端执行 [ -d /dev ] && echo "is dir" , 则输出 “is dir”

整数比较

在这里插入图片描述

一个例子:

[ 100 -gt 99 ]
echo $?

输出 0

另一个较为综合的例子:

FreeMem=`free -m | grep Mem: | awk '{print $4}'`
[ $FreeMem -lt 1024 ] && echo "Insufficient Memory"

用于判断系统剩余内存是否小于 1024 M ,这条语句用到了管道命令 |awk 命令(处理文本)

流程控制

if 语句

单分支

DIR="tmp"
if [ ! -e $DIR ]
then mkdir -p $DIR
fi

双分支

DIR="tmp"
if [ ! -e $DIR ]
then mkdir -p $DIR
else echo "tmp exists!"
fi

多分支

if xxx
	then xxx
elif xxx
	then xxx
else 
	xxx
fi

for 语句

语法:

for var in value_list
do 
	xxx
done

给出一个批量建用户名的例子:

read -p "Enter The Users Password : " PASSWD   # read 接收用户输入
for UNAME in `cat users.txt`  # 反引号,当作命令执行,并返回结果
do
id $UNAME &> /dev/null
if [ $? -eq 0 ]  # 判断上一条指令是否成功执行
then
echo "Already exists"
else
useradd $UNAME &> /dev/null
echo "$PASSWD" | passwd --stdin $UNAME &> /dev/null   # 管道命令,将前一条命令的输出,作为后面命令的输入参数
if [ $? -eq 0 ]
then
echo "$UNAME , Create success"
else
echo "$UNAME , Create failure"
fi
fi
done

在脚本中使用 read 命令读取用户输入的密码值,然后赋值给 PASSWD 变量,并通过 -p 参数向用户显示一段提示信息,告诉用户正在输入的内容即将作为账户密码。在执行该脚本后,会自动使用从列表文件 users.txt 中获取到所有的用户
名称,然后逐一使用“id 用户名”命令查看用户的信息,并使用 $? 判断这条命令是否执行成功,也就是判断该用户是否已经存在。需要多说一句, /dev/null 是一个被称作 Linux 黑洞的文件,把输出信息重定向到这个文件等同于删除数据(类似于没有回收功能的垃圾箱),可以让用户的屏幕窗口保持简洁

注:shell 中的重定向

0表示标准输入
1表示标准输出
2表示标准错误输出
> 默认为标准输出重定向,与 1> 相同
2>&1 意思是把 标准错误输出 重定向到 标准输出.
&>file 意思是把 标准输出 和 标准错误输出 都重定向到文件 file 中

while 语句

语法:

while xxx
do xxx
done

例子

PRICE=$(expr $RANDOM % 1000)  # expr 表达式求值,常用于数学运算
TIMES=0  # 等号两侧不能有空格
echo "商品实际价格为 0-999 之间,猜猜看是多少? "
while true
do
read -p "请输入您猜测的价格数目: " INT
let TIMES++  # let 用于数学计算
if [ $INT -eq $PRICE ] ; then
echo "恭喜您答对了,实际价格是 $PRICE"
echo "您总共猜测了 $TIMES 次"
exit 0
elif [ $INT -gt $PRICE ] ; then
echo "太高了! "
else
echo "太低了! "
fi
done

注:

  • 等号两侧不能有空格
  • 原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用
  • PRICE=$(expr $RANDOM % 1000) 等价于 PRICE=`expr $RANDOM % 1000`, 使用 expr 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2
  • let 也可用于运算,let s=(2+3)*4 , let no++, let no+=10 let 不需要空格隔开表达式的各个字符

常用指令

  • 文本编辑指令
    cat xxx
    more xxx
    head/tail -n 10 xxx  # 最前或最后 10 行
    cat anaconda-ks.cfg | tr [a-z] [A-Z]  # tr 替换字符
    wc -l/w/c xxx  # wc 命令用于统计指定文本的行数、字数、字节数
    stat xxx  # 命令用于查看文件的具体存储信息和时间等信息
    cut -d: -f1 /etc/passwd  # 命令用于按“列”提取文本字符,格式为“ cut [参数] 文本”
    diff a.txt b.txt  # 比较不同
    tr -s 替换字符
       -d 删除字符
    sort 按行排序
        -r 反向排序
    uniq -c 统计每行词频
    awk 动作 文件名 # 实施动作到每一行
    

shell 编程注意事项

  • 变量引用必加 “$”
  • echo 自动换行
  • printf fmt args,… 类似 C 语言
  • 区分某个命令的行为
    • 对参数操作 echo/pringf
    • 对输入流操作 cat/read
    • 是否操作输出流 cat/echo
args == echo ==> stdout
args == printf ==> stdout
stdin == cat ==> stdout
stdin == read ==> args

shell 编程案例

  • for 循环 1
for x in {1..100..2}
do
   echo $x
done
  • for 循环 2

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

“(())” 内部可以不加 ‘$’

  • 条件判断 1
read x
read y
if ((x<y))
   then echo X is less than Y
elif ((x==y))
   then echo X is equal to Y
else
   echo X is greater than Y
fi
  • 条件判断 2
read c
if [ $c = "Y" ] || [ $c = "y" ]
   then echo YES
else 
   echo NO
fi
  • 实现 eval 函数
read e
echo $e | bc -l | xargs printf "%.3f" 
# 以上等价于
printf "%.3f\n" $(echo $(cat) | bc -l)
  • 函数定义
# 定义
sum() {
    echo $* | tr ' ' '+' | bc -l | xargs echo 
}

# 调用
sum 1.544 4.7 8.7777