Skip to content

Shell

shell是一种命令行解释器,它用来将用户指令翻译给内核,将内核结果翻译给用户。以降低使用成本,保护内核安全。

bash script.sh, ./script.sh, source script.sh,前两种执行方式是交给子进程执行可以隔离执行环境,后者作用于本次会话。

1. 变量

变量分类

shell中的变量有四种,分别是本地变量、环境变量、全局变量、系统变量。

类型解释
本地变量在shell中定义的普通变量,仅在本shell中有效
环境变量环境变量对本shell以及子进程有效
全局变量用户定义在配置文件内的变量,启动会话时会自动导入,执行顺序为profile, bash_profile, bashrc, bash_logout
系统变量系统定义的指定名称的变量,如SHELL,PWD,HOME,以及如下的特殊变量
特殊变量
$?值为上一条命令的返回状态
$1-$9用来获取命令行参数,$0表示脚本名,表示第一到第九的参数,十以上用$
$#命令行参数的个数,不包含脚本名
$*所有命令行参数,不包含脚本名,作为一个整体字符串
$@所有命令行参数,不包含脚本名,每个参数分开字符串
$$获取当前进程的父进程的PID

自定义变量

自定义变量方式
定义变量变量名=值,赋值符左右不能有空格,默认是字符串类型
删除变量unset 变量名
定义只读变量readyonly 变量名=值,只读变量不可被unset
导入环境变量export 变量名
定义环境变量export 变量名=值
根据类型定义变量declare -类型 变量名=值,-i 整型,-r 只读,-a 普通数组,-A 关联数组,-x 环境变量

2. 运算符

运算符解释
+ - \* / %\*表示乘号,**表示次幂
exprexpr `expr 2 + 3` \* 4,运算符前后要求空格
$(()) $[]$(((2+3)*4))$[(2+3)*4])
letlet加整数运算表达式,let a++, let a+=1等

常用通配符

通配符含义解释
*匹配0或任意个字符
?匹配单个字符
[]匹配[]内任意单个字符[abc]表示该位置可以是a,b,c中的任意一个字符,[a-z]表示可以是a-z中的任意一个字符
[!]抛弃[]内任意单个字符
{}匹配{}内的任意一组字符串{1,2,123},该位置可以是1,2,123

运算式的引号

引号解释
双引号 "将引号内容当作整体,允许$引用其他变量
单引号 '将引号内容当作整体,不允许引用其他变量,所有字符视为原始字符
反引号 `和$()一致,将里面的命令或表达式优先执行

3. 条件

条件格式解释
test 条件,[ 条件 ]两种方式作用一致
[[ 条件 ]]支持正则表达式
条件解释
数值比较-lt 小于,-le 小于等于,-gt 大于,-ge 大于等于,-eq 等于,-ne 不等于
(( )) 类C风格条件表达式
文件类型-e 文件存在,-f 存在且为普通文件,-d 存在且为目录
文件权限-r 具有读权限,-w 具有写权限,-e 具有执行权限
文件新旧file1 -nt file2 file1比file2更新,-ot 更旧,-ef 是否为相同文件(同一个inode)
字符串-z 为空,-n 非空,= 相等 != 不相等
逻辑与&&,-a
逻辑或||,-o

4. 流程控制

4. 分支

if

shell
if [ $1 -lt $2 ]; then
    echo "$1 -lt $2"
elif [ $1 -eq $2 ]; then
    echo "$1 -eq $2"
else
    echo "$1 -gt $2"
fi
shell
if [ $1 -lt $2 ]
then
    echo "$1 -lt $2"
elif [ $1 -eq $2 ]
then
    echo "$1 -eq $2"
else
    echo "$1 -gt $2"
fi
shell
if [ cond1 ] && [ cond2 ]; then # 多个条件
fi

case

shell
case $1 in
0)
    echo 'female'
;;
1)
    echo 'male'
;;
*)
    echo 'unkown'
;;
esac

5. 循环

for

shell
sum=0
for (( i=0; i < 3; i++)) # 类C风格循环
do
    sum=$[$sum + $i]
done
echo $sum
shell
for v in 1 2 3 4 # 列表循环
do
    echo $v
done
for v in {1,2,3,4}
do
    echo $v
done
shell
for v # 将命令行参数作为循环列表,等同于 for v in '"$@"'
do 
	echo $v
done

while

shell
i=0
while [ $i -lt $1 ] # while :; 死循环
do
    echo $i
    i=$[$i+1]
done

until

shell
i=0
until [ $i -ge $1 ]
do
  echo $i
  let i++
done

shift

shift用于将参数列表向左移动一位,也就是删除第一个参数,后续参数向前移动。

位于整个文件就是作用于命令行参数,位于函数内就是作用于函数参数。

shell
echo $*
shift
echo $*

function test_shift() {
    echo $*
    shift
    echo $*
}
test 1 2 3 4 5

expect

expect用来代替用户的交互行为,做到自动化,通常作用于交互式的程序中。

shell
#!/bin/expect

set sip "localhost"
set user "yyx"
set pswd "YYXyyx123456"

spawn ssh $user@$sip

expect {
    "yes/no" { send "yes\r"; exp_continue }
    "password" { send "$pswd\r" }
}

expect "$"
send "date\r"

5. 输入输出

命令解释
read标准输入读取,-p 打印提示,-t 等待事件,-s 输入不回显,搭配<读取文件内容
echo标准输出打印,-e 内含转义字符,-n 禁止自动换行

6. 函数

函数分为系统函数和自定义函数。

系统函数解释
basename string [suffix]截取出路径最后的文件名,指定后缀后也可截去后缀
dirname string截取出路径的目录部分,结果不以/结尾
shell
function funcname()
{
	statement
	return
}
  • 函数返回值只能通过 $? 获得。如果函数最后没有return,则以最后一条命令的运行结果作为函数返回值。
  • 在函数内部,$1 $2 仅代表函数参数,不会和命令行参数冲突。

7. 数组

shell的数组有两种:

  • 普通数组:只能使用整数作为数组下标。
  • 关联数组:可以使用字符串作为数组下标,本质是哈希。
普通数组定义解释
arr[0]=xx; arr[1]=xx; ...定义然后添加元素
arr=(v1 v2 v3 ...)定义并初始化
arr=(`ls`)将命令结果按空格或换行分割,并作为数组元素
普通数组获取元素解释
$按下标获取数组元素
${arr[*]} $获取所有元素
$获取数组元素个数
$获取数组元素下标
$根据起始位置和偏移量,获取部分数组元素
关联数组定义解释
declare -A arr; arr[str]=xx; ...定义然后添加元素
declare -A arr; arr=([str]=xx ...)定义并初始化
关联数组获取元素解释
$获取数组元素
${arr[*]} $获取所有元素
$获取数组元素个数
$获取数组元素下标

8. 文本处理

命令解释
grep string file行过滤,-v 反向过滤,-n打印行号,-i忽略大小写,-B 指定头几行,-A 尾几行,-C 头尾几行
cut -f int -d string file列过滤,-f 指定列号,-d 指定分割符默认为\t
sort行排序,-u 去重,-r 降序默认升序,-n 按数字排序,-k 按指定列排序,-t 分隔符,-o 结果输出文件

正则表达式中,^$分别表示行首和行尾,^abc表示以abc开头,abc$表示以abc结尾。

sed

sed是流文本编辑器,就是将文件按行处理,shell脚本中使用sed来精准修改文件。

选项解释
-e指定脚本表达式,也就是对文本的处理方法。脚本中不指定行表示对所有行生效。
增:分为行前新增和行后追加,i表示行前新增,a表示行后追加。
删:d表示删除整行。
改:分为覆盖整行和替换部分,c表示覆盖整行,s表示替换部分。
查:p表示查看。
-i默认不会修改源文件,-i表示将结果输出到源文件,-i跟内容表示修改并生成备份文件,-i内容是备份文件名的后缀。
-n-n '1p':忽略缓冲区,仅打印文件的第一行内容。
-f-e是手写脚本,-f是指定sed脚本文件
shell
sed -e '1i\first line'  test.txt # 第一行前新增一行first line
sed -e '1a\second line' test.txt # 第一行后追加一行second line

sed -i    -e '1i\first line' test.txt # 将结果输出到源文件
sed -i.bk -e '1i\first line' test.txt # 修改源文件之前生成一份备份文件,名为test.txt.bk

sed '1d' test.txt # 删除第一行

sed '1c\first line' test.txt # 修改第一行为first line 

sed '1s/1/2/'  test.txt # 第一行的1替换成2,只查找替换一次
sed '1s/1/2/g' test.txt # 第一行中全部的1都替换成2

sed -n '1p' test.txt # 忽略缓冲区,仅打印文件的第一行内容