Unix/Linux Shell
本文主要是参考书上内容并总结出来的,因此无法全面概述每一个shell的使用,具体可以man查看帮助。👀
rm
玩过linux都知道 rm 这个命令,它是用来删除一个或多个文件/目录,一般常用的操作是 rm files。不过有时为了省事,直接 rm -rf files/dirs表示强制删除文件或目录(包括子目录所有文件)。
不过有时候突然sb了,直接执行 rm -rf /* ,除非反应在0.000000009ms内否则基本就GG了,还要一种写法也可能GG,比如: rm -rf $TMP/*,如果变量$TMP 不存在或为空,那么这条命令等价于rm -rf /*。。。
总之,rm就是万恶之源,远离rm,从我做起 🤣
变量初始化空值
Linux变量初始化为空值有3种形式,效果一样。
var=var=""(无空格)var=''(无空格)
整数算数操作 $(())、$[]
基本上linux shell提供了一种叫做算术扩展的机制能够执行shell变量的整数运算。(PS: bash仅支持整数,而zsh其实支持浮点数的。以下所有命令及结果均在bash上测试)
格式: $((expression)) 或 $[expression] expression表达式首尾可无空格
$(()) 支持 +、-、、/、%、**(幂)、+=、-=、=、/=、var++、var–、++var、–var
除此之外,$(())也支持进制之间的转换(转换为10进制)。即 $((基数#转换的值))
1 | |
除此之外,可以在if/for内进行求和,比如下面这个简短的例子,其中 : 不能去掉,因为如果没有:那么$((sum+=i))被当作一条命令去执行,而这里我们只需要求和,没有执行什么shell命令,于是bash只能报错未找到命令。
1 | |
命令替换 $()
众所周知,linux通过 $() 和 ` `来执行一些命令,比如echo $(date) echo `date`,这在shell脚本中很常见,可以保存输出结果到变量中。
变量替换 ${}
所谓的变量替换也就是把变量放在用双引号""字符串而不是单引号''字符串中。这是因为单引号表示告诉shell忽略字符串中的特殊字符,比如$
比如
1 | |
参数替换
shell中参数包括:传递给程序的参数(位置参数,比如$0、$1…)、特殊的shell变量(如 $#)和普通变量(关键字参数)
| 参数 | 含义 |
|---|---|
| ${parameter} | 替换为parameter的值 |
| ${parameter:-value} | 如果parameter已设置且不为空,替换为它的值;否则,替换为 value |
| ${parameter-value} | 如果parameter已设置,替换为它的值;否则,替换为 value |
| ${parameter:=value} | 如果parameter已设置且不为空,替换为它的值;否则,替换为 value并将其赋给parameter |
| ${parameter=value} | 如果parameter已设置,替换为它的值;否则,替换为 value并将其赋给parameter |
| ${parameter:?value} | 如果parameter已设置且不为空,替换为它的值;否则,将value写入标准错误并退出。若忽略value,则向标准错误写入parameter: parameter null or not set |
| ${parameter?value} | 如果parameter已设置,替换为它的值;否则,将value写入标准错误并退出。若忽略value,则向标准错误写入parameter: parameter null or not set |
| ${parameter:+value} | 如果parameter已设置且不为空,替换为value;否则,替换为空 |
| ${parameter+value} | 如果parameter已设置,替换为value;否则,替换为空 |
| ${ #parameter} | 求parameter的长度 |
| ${parameter#pattern} | 从parameter左边开始删除pattern的最短匹配,余下内容作为参数替换的结果 |
| ${parameter##pattern} | 从parameter左边开始删除pattern的最长匹配,余下内容作为参数替换的结果 |
| ${parameter%pattern} | 从parameter右边开始删除pattern的最短匹配,余下内容作为参数替换的结果 |
| ${parameter%%pattern} | 从parameter右边开始删除pattern的最长匹配,余下内容作为参数替换的结果 |
通过 set 命令可以为位置参数重新赋值。
如 set a b 123,这将a赋值给$1、b赋值给$2、123赋值给$3。
还可以将输入的每个数据依次分配到位置参数$1、$2…
1 | |
数学等式解算器expr
expr命令没有$(())高级,功能比较局限而且也只支持整数。expr除了支持基本的 ±*/外,还支持|、&、!=、=、<=、<、>=、>逻辑判断。需要用空格分隔参数,而且乘的话需要用\转义!注意不要理所当然地用 "" 来包围表达式,这没用
1 | |
当然expr还有其他功能,比如
1 | |
传递参数
可以给一个shell脚本或函数传递参数,与获取参数有关的一些变量大致如下
| 变量 | 变量 | ||
|---|---|---|---|
| $# | 参数个数 | ${1}~${n} | 参数1~n |
| $* | 所有原始参数 | $@ | 所有参数且每个参数用""包围,如果"$1" |
当然还有其他的变量没有列出。
shift
shift命令可以左移位置参数,即解开参数包,每次执行shift $#自动减1,$2赋予给$1,$3赋予给$2… 当$#=0,没有参数可解时报错,具体情况根据shell的不同而不同。比如我这里bash没有显示报错信息,但$?为1,而zsh显示报错信息且$?为1。这个例子展示了如何利用shift一次解开参数包,当然也可以用${1}~${n}
1 | |
条件测试test
test命令可以用来测试两个字符串是否相等,而且test会把测试变量当作字符串来看待,也就是说 test 100 = 100实际上是test "100" = "100"或者 test "100" = 100。
由于测试的结果是上一条命令执行的退出状态码,因此结果普遍是0真,非0假。
test 字符串的其他一些操作符
| 操作符 | 说明 |
|---|---|
| str1 = str2 | str1 等于 str2 ? |
| str1 != str2 | str1 不等于 str2 ? |
| str | str 不为空? |
| -n str | str不为空 ? |
| -z str | str为空 ? |
关于str和-n str区别,举一个例子就知道了
1 | |
即可以把-n str看作是测试字符串长度是否大于0。
条件测试 []、[[]]、(())
除了在if中用test判断条件之外,还可以用[ ]和[[ ]]测试条件。
注意,[是一个命令位于 /usr/bin/[ ,而[[ ]] 是bash的一个关键字。注意[和]内首尾空格。
常规写法
1 | |
使用[ ]
1 | |
上面两种写法不够简洁,因此可以使用[[ ]]
1 | |
甚至还可以使用C风格的条件判断
1 | |
整数比较
| 操作符 | 说明 |
|---|---|
| n1 -eq n2 | n1 等于 n2 ? |
| n1 -ne n2 | n1 不等于 n2 ? |
| n1 -ge n2 | n1 大于等于 n2 ? |
| n1 -gt n2 | n1 大于 n2 ? |
| n1 -le n2 | n1 小于等于 n2 ? |
| n1 -lt n2 | n1 小于 n2 ? |
1 | |
总之,在比较字符串和整数时要注意操作符
文件操作符
| 操作符 | 说明 |
|---|---|
| -d file | file是一个目录 |
| -e file | file存在 |
| -f file | file是一个普通文件 |
| -s file | file不是空文件 |
| -r file | file可读 |
| -w file | file可写 |
| -x file | file可执行 |
| -L file | file是一个符号链接 |
| -g file | file设置了SGID位 |
| -u file | file设置了SUID位 |
| -p file | file是一个命名管道 |
| -S file | file是一个套接字 |
1 | |
调试选项 -x
bash可以制定-x选项来跟踪执行过程,比如有下面这个shell脚本
1 | |
通过 bash -x test.sh 跟踪执行过程
1 | |
或者set -x打开跟踪模式、set +x关闭跟踪模式
空命令 :
前面有谈到过:这个命令,它就像Python的pass,类似占位符,什么都不做。有时可以用:在if/for/while/until/case内执行一些赋值操作
.命令
. file会使Shell读取并执行指定的文件,file不一定是可执行的,只有可读就行。
参数扩展
1 | |
占位符、注释
1 | |
函数名
1 | |
比如众所周知的fork bomb :(){ :|:& };: 就是如此简单粗暴
循环
跳过/跳出循环
continue n或break n来跳过或跳出第n层循环
后台执行循环
在循环关闭语句done后面加上&能够使循环for/while后台执行
1 | |
循环上的IO重定向
输出重定向
1 | |
输入重定向
1 | |
printf
对于简单的信息echo足矣,但是有时候需要格式化输出信息,那么linux提供了一个类似C的printf命令来帮助完成格式化,更多内容可以去查看帮助文档😹
1 | |
exec
exec除了会用新程序替换现有的程序,还有一下功能
重定向标准输入
exec < infile
1 | |
重定向标准输出
exec > outfile
1 | |
重置标准输入/输出
exec > /dev/ttyexec < /dev/tty
命令组( … )、{ …; }
( ... )内的命令会在子shell执行,而{ ...; }内的命令则是在当前shell执行。
要注意,{ ...; }的左{后需要空格!
1 | |
其实{ ...;}就像一个无名函数的调用…
只读变量
readonly和declare命令可以指定某个变量只读。
readonly var或declare -r var
eval
把eval放在命令行之前,Shell会对该命令行进行二次扫描,然后执行。
1 | |
Shell第一次扫描时把pipe变量替换为|,然后eval使得Shell重新扫描命令行,此时Shell能够识别出|是一个管道符号,因此便能成功执行该命令行
与eval搭配的字符有;、|、&、<、>、引号' "
下面是一个典型的例子,展示了shell中所谓的“指针”
1 | |
trap
trap用于信号处理,一般格式 trap commands signals
一些信号列表如下
| 信号 | 信号名称 | 产生原因 |
|---|---|---|
| 0 | EXIT | 退出SHELL |
| 1 | HUP | 挂起 |
| 2 | INT | 中断 |
| 3 | QUIT | 退出 |
| 6 | ABRT | 中止 |
| 9 | KILL | "销毁"进程 |
| 14 | ALAM | 超时 |
| 15 | TERM | 软件终止信号 |
没有参数的trap会显示定义过或修改过的所有trap处理程序
1 | |
trap "" SIGINT 忽略SIGINT信号,这也会导致所有的子shell也忽略这个信号(无论子shell是否有自己的trap信号处理程序)
trap : SIGINT 当前shell什么也不做,子shell执行默认的信号处理程序(如果有的话)
trap SIGINT 重置中断信号处理
一、
1 | |
二、
1 | |
IO重定向
下表展示了重定向的一些操作(从左到右处理重定向)
| 符号 | 说明 |
|---|---|
| < | stdin重定向 |
| > | stdout重定向 |
| 2> | stderr重定向 |
| >&2 | stdout重定向到stderr |
| > file 2>&1 | stdout和stderr重定向到file,等价于 >file 2>>file |
| >&- | 关闭stdout,stderr,如 exec 1>&-、exec 2>&- |
| <&- | 关闭stdin,如 exec 0<&- |
比如常见的 commands >/dev/null 2>&1
行内输入重定向 here documents
主要有以下几种形式
<<EOF忽略特殊字符<<\EOF不忽略特殊字符,也就是原封不动的输出输入的内容<<-EOF删除输入内容中的前导制表符<<EOF>>如cat <<EOF>>outfile<<EOF|tee如cat <<EOF|tee outfile
1 | |
1 | |
删除变量和函数
unset var
unset -f func
退出函数
return n n为函数返回状态,可以用$?捕获
局部变量
使用typeset或local定义局部变量,其中local只能用在函数中。
同时typeset还可以定义整数类型的变量-i,若赋值的不是一个整数,那么可能会提示错误
1 | |
数组
1 | |
生成伪随机数
/dev/random在类UNIX系统中是一个特殊的设备文件,可以用作随机数发生器或伪随机数发生器。
/dev/random的一个副本是 /dev/urandom(“unblocked”,非阻塞的随机数发生器),它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它可以作为生成较低强度密码的伪随机数生成器,不建议用于生成高强度长期密码。
详细信息维基百科/dev/random
1 | |
限制文件列宽 fold
1 | |
判断命令是否存在
可以用于判断linux命令是否存在的方法很多,比如 which、type、hash、command
1 | |
结尾
大概就这样吧…👴💔
以后在补充补充🍑
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!