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/tty
exec < /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 协议 ,转载请注明出处!