右脚前掌踏入shell领域!
shell 的版本
一个 shell 脚本第一行第一件事是 “确定本个脚本用什么 shell 执行”。
##! bin/bash
这是比较普遍的,用 bash 执行脚本。
还有一些其他的 shell ,他们在 /bin/ 目录下,比较常见的有 tcsh / zsh / csh / sh 。他们在擅长领域有所不同。在用户登录时,系统通过 /etc/password 下面登录用户条目的第七个字段选择默认的 shell。
结构化命令!
test 命令
if 是最基础做判断用的结构语句。关于它的判断有两种形式
- test condition
- [ condition ]
讲讲 condition! :
类型 | 判断符号 | 备注 |
---|---|---|
整数 | -eq , -ne , -ge , -le , -gt , -lt | 只能比较整数 |
字符串 | > , < , = , != | ①在 [ ] 中要转义大小写,以免变成重定向符号;②condition中是根据ASCII码进行比较,Sort是根据本地字符 |
文件 | -r , -w , -x , -d , -f , -e , -O ,-G , -nt , -ot | 无 |
高级 test condition:
-
(( condition ))
- 不用转义大于小于号
- 是做数字运算的,拥有很多很好的高级功能
-
val++
val--
- 幂次方:
**
- 或与:
||
&&
- 位的与或非、取反、左移右移:
&
|
!
^
<<
>>
-
-
[[ condition ]]
- 是处理字符串的,在基础功能上添加了模式匹配功能
e.g: [\[ $user == r* ]] ##查找user中所有r开头的项
处理命令行输入!
参数
其他的语言,比如讲 C,将参数传入函数前,需要在函数名后声明参数的数量和名字,比如:
int main(int a,char b){...}
shell 不需要这种声明。直接在函数后面跟要传入的参数就好
$./main.sh a b
传入的参数最多可以有 9 个,在代码中,用 $x 的方式来指代第 x 个参数。
有这些关于参数的取用方法:
- $x ##第x个参数
- $* ##所有参数(一次性抓取)
- $@ ##所有参数(按分隔符分次抓取)
- $## ##参数的数量
$0 取到脚本名,用 basename( $0 ) 可以过滤掉执行脚本命令前面的路径,取到纯粹的脚本名。
选项
为了方便操作,最好是在命令下提供一些精细化选项,(比如 ls -al
的-al
- 用 case 实现是最基础的方法:
while [ -n $1]
do
case "$1" in
a) <cmd> ;;
b | c) <cmd> ;;
*) <cmd>;;
esac
shift ##参数左移一位
done
通过这样一个代码块就可以实现最基础的命令选项功能了。
不过一个贴心靓仔还需要更加以人为本一些。为了处理 -xyz
这样的用户输入,还需要使用一些工具的帮助。
-
使用 getopt 的帮助:
getopt可以识别命令行参数,方便在脚本中的解析。格式如下:
getopts <optstring> <parameters>
- optstring:定义了命令行的有效选项字母,还定义了选项需要参数值(用 ':' 分隔)
- paramteres:是参数值
脚本用法:
set -- $( getopt ab:cd "$@" )
//脚本内容...//
假如传入的选项是 -xyz
,传入后会被 getopt 格式化成 -x -y -z
再通过set替换原来的选项。最后再为脚本所用。
但是getopt也有局限性!它不能处理带分隔符的参数,如果参数是"test3 test4"
,它会把这个参数分别看成两个带引号的参数。这个时候就需要更高一级的getopts了。
-
supreme getopts:
getopts <optstring> <variable>
和getopt选项和参数处理后只产生一个输出不同,getopts一次只处理一个检测到的参数,处理完所有的参数之后,结束,返回状态码0。
这个特性让它很适合用在 while 之类的循环中。while getopts ab:c opt ##opt储存getopts参数内容 do case "$opt" in /.../ esac done
getopts 有俩环境变量
- OPTARG:如果选项有参数,保存这个参数值
- OPTINT:现在在处理的参数位置,每处理一个选项 +1。处理完选项之后可以用 shift OPTINT-1 ]移动到参数位置
getopts 允许在输入的参数值中使用空格;允许选项和参数中间不隔空格;没有定义在 optstring 中的选项会输出成 '?'。非常友善,非常好用。
输入输出重定向
标准文件描述符
Linux 将每个对象都当做文件处理,这也包括了输入和输出进程。通过文件描述符,Lunix可以对每个文件对象进行标识。
每个进程一次最多可以有 9 个文件标识符,不过 bash 只保留了前三个:
文件描述符 | 缩写 | 描述 | 最初定位 | 符号 |
---|---|---|---|---|
0 | STDIN | 输入 | 键盘 | < |
1 | STDOUT | 输出 | 屏幕 | > |
2 | STDERR | 错误 | 屏幕 | > |
可以单独重定位:
ls -al test badtest test1 2> test2
-rw-r--r-- 1 陈辛辛 197121 53 5月 14 17:39 test2
--------------------------------------------------------------------
$ cat test2
ls: cannot access 'test1': No such file or directory
ls: cannot access 'badfile': No such file or directory
也可以全部重定向:
$ ls -al test1 badtest test2 &> test3
--------------------------------------------------------------------
$ cat test3
ls: cannot access 'test1': No such file or directory
ls: cannot access 'badtest': No such file or directory
-rw-r--r-- 1 陈辛辛 197121 108 5月 14 17:39 test2
临时重定向和永久重定向
-
临时重定向
当需要使用临时重定向功能时,在文件描述符前加 '&' :1 > &2 #将1的输出重定向为2.它和 &> 是不同的东西。 &> 意味将 STDOUT 和 STDIN 输出重定向为同一个文件。
-
永久重定向
当有大量数据需要重定向的时候,需要每行数据都要重新定位的临时方法就显得不太得体。此时需要用 exec 命令在脚本的执行期间重定向某个特定的文件描述符。
由上文所述bash 只启用了 0~2 的文件描述符
,但实际上3~8 也是可以用的。一个 exec 的例子:
exec 3 > &1 ##3 的输出状态重定向为 1(STDOUT) exec 1 > testfile ##1 的输出重定向为testfile echo "have try" exec 1 > &3 ##1 的输出重定向为3(STDOUT)
上文中的 "have try" 最终是在 testfile 中显示
-
如果想要关闭某个文件描述符,将其重定向为 &-
exec 3 > &- echo "test" > &3 --------------------------- 报错:Bad file discriptor
关闭后就不能使用该文件描述符写入数据啦
控制脚本
linux信号
信号 | 值 | 描述 |
---|---|---|
1 | SIGUP | 挂起进程 |
2 | SIGINT | 终止进程 |
3 | SIGQUIT | 停止进程 |
9 | SIGKILL | 无条件终止进程 |
15 | SIGTERM | 尽可能终止进程 |
17 | SIGSTOP | 无条件停止,但不是终止进程 |
18 | SIGTSTP | 停止或暂停,但不终止进程 |
19 | SIGCONT | 继续运行停止的进程 |
信号的操作!
-
ctrl+c
=> SIGINT
终止进程 -
ctrl+z
=> SIGTSTP
停止进程。停止的进程用ps
还是能够查看到的,state 状态为T的就是停止的状态。如果想要终止停止的进程,可以 exit 退出shell,还可以通过 PID kill掉进程。 - trap
可以抓进程。格式是trap <command> <signame>
,当捕捉到 signame ,运行cmd的内容。
作业控制
-
jobs
可以查看现在正在处理的作业,不论作业是停止还是运行,前台还是后台。
把作业放在后台运行可以使用&
命令。 nohup
命令起到辅助作用,它可以阻断所有发送给该进程的SIGHUP信号,就算终端退出了也不停止,不仅如此它还会自动把输出 >> 到一个叫做 nohup.out 的文件,使输出不会哐哐往屏幕上蹦。
虽然jobs本人对作业的状态没什么影响,但是很多命令需要它的帮忙。
bg把暂停命令在后台重启;fg把暂停命令前台重启;kill杀死进程;
都需要jobs查看他们的PID
-
定时运行
- at
非常方便的定时执行命令,可惜是一次性的。支持HH:MM,AM/PM,MMDDYY,文本日期和指定时间增量。 - cron
<min,hour,dayofmonth,month,dayofweek> cmd
可以周期性地执行命令。
crontab命令可以列出 cron 时间表,另外cron自己也有目录。在 /etc/ 目录下,有cron.daily / cron.weekly / cron.hourly / cron.monthly 四个目录,不要求精确运行的前提下,把脚本复制进这几个文件是最方便的定时周期执行的方法。
- at