Shell脚本进阶
一、显示数据
1.1 理解输入和输出
- 标准文件描述符
0
STDIN 标准输入- 就是从键盘(通常是
/dev/tty
)获取数据。 - 使用重定向时,会使用重定向指定的文件来替换STDIN
- 就是从键盘(通常是
1
STDOUT 标准输出- 就是输出到终端显示器(一般是
/dev/tty
) - 使用重定向时,可以将输出到指定的文件中。
- 就是输出到终端显示器(一般是
2
STDERR 标准错误- 运行的程序和脚本出错时生成的错误消息都会发送到STDERR
- 默认情况下,STDERR也是输出到终端显示器
- 使用
2>
重定向,例如ls -al 不存在的文件 2> 收集错误的文件
。 - 同时重定向STDERR和STDOUT
ls -al 不存在的文件 存在的文件 2> 收集错误的文件 1>正确的文件
- 也可以
ls -al 不存在的文件 存在的文件 &>同一个文件
,错误的内容会放在前面。
1.2 在脚本中重定向输出
- 临时重定向
- 将STDOUT重定向到STDERR
echo "this is error" >&2
- 原本是输出到STDOUT,现在输出到STDERR
- 将STDOUT重定向到STDERR
- 永久重定向
exec 1>out.log
,所有的STDOUT都重定向到out.log中。exec 2>err.log
,所有的STDERR都重定向到err.log中
1.3 在脚本中重定向输入
exec 0< file
,接下来通过STDIN获取数据,都会通过file来获取。
1.4 创建重定向
-
创建输出文件描述符
exec 3>test3out
-
重定向文件描述符
-
例子
#下面是脚本内容 $ cat test14 #!/bin/bash exec 3>&1 # 首先 创建了文件描述符3,然后指向1指向的位置 exec 1>testout # 然后1指向的位置改成testout文件 # 因为echo默认都是输出到1,所以现在输出到了testout文件 echo "This should store in the output file" echo "along with this line." # 然后把1重新指向3指向的,也就是终端显示器 exec 1>&3 # echo输出到1,也就是终端显示器了。 echo "Now things should be back to normal" # 下面是执行脚本的结果 $ ./test14 Now things should be back to normal $ cat testout This should store in the output file along with this line. $ -
文件描述符就可以看作是指针,指向一个区域。
-
-
创建输入文件描述符
- 例子
#!/bin/bash # 先将原来从键盘输入保存起来 exec 6<&0 # 将STDIN指向testfile文件 exec 0< testfile count=1 while read line do echo "Line #$count: $line" count=$[ $count + 1 ] done # 将STDIN指回键盘输入 exec 0<&6 read -p "Are you done now? " answer case $answer in Y|y) echo "Goodbye";; N|n) echo "Sorry, this is the end.";; esac
- 例子
-
创建读写文件描述符
-
例子
$ cat test16 #!/bin/bash #创建文件描述符 可以同时读写 exec 3<> testfile # 从文件中读取一行数据 read line <&3 # 输出到STDOUT,也就是终端显示器上 echo "Read: $line" # 输出到文件描述符3,也就是testfile中 echo "This is a test line" >&3 # 查看原来的testfile文件内容 $ cat testfile This is the first line. This is the second line. This is the third line. # 执行脚本 $ ./test16 Read: This is the first line. # 查看现在的testfile文件 $ cat testfile This is the first line. This is a test line ine. This is the third line. $ -
注意到,testfile文件的一部分被修改了,是因为读取文件时,有一个指针指向了第二行数据。然后又写,所以把第二行数据的一部分给覆盖了。
-
-
关闭文件描述符
- 语法
exec 3>&-
- 关闭了之后,就不能读写了。如果再创建一个文件描述符,指向同一个文件,此时会新创建一个文件,覆盖原有的文件。
- 语法
1.5 列出打开的文件描述符lsof
-
-p
指定进程ID -
-d
指定要先的文件描述符编号 -
-a
对其他两个选项的结果执行AND运算 -
例子:
lsof -a -p $$ -d 0,1,2
-
$$
表示当前shell的进程ID -
结果
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME bash 5474 tom 0u CHR 136,1 0t0 4 /dev/pts/1 bash 5474 tom 1u CHR 136,1 0t0 4 /dev/pts/1 bash 5474 tom 2u CHR 136,1 0t0 4 /dev/pts/1 COMMAND
: 正在运行的命令名的前9个字符PID
:进程PIDUSER
:进程属主的登录名FD
:文件描述符号以及访问类型(r读、w写、u读写)TYPE
:文件的类型(CHR字符型,BLK块型,DIR目录,REG常规文件)DEVICE
:设备的设备号(主设备和从设备号)SIZE/OFF
:如果有,则代表文件的大小NONE
:本地文件的节点号NAME
:文件名
-
1.6 阻止命令输出
- 可以将输出重定向到
/dev/null
文件中,输出到null文件的任何数据都不会保存。 - 还可以
cat /dev/null > file
,将文件内容清空。
1.7 记录消息tee命令
- 同时将输出发送到显示器和日志文件
命令 | tee 日志文件
- 会覆盖文件
-a
追加文件
1.8 使用例子
-
读取.csv数据,然后生成插入数据库的语句
-
#!/bin/bash # read file and create INSERT statements for MySQL outfile='members.sql' IFS=',' while read lname fname address city state zip do # 这里是关键 # 将cat的结果输入到文件中 # 后面又使用EOF作为文本的结束标志,也就是使用后面一大坨的内容作为cat的输入 cat >> $outfile << EOF INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('$lname', '$fname', '$address', '$city', '$state', '$zip'); EOF done < ${1} # 注意这里 表示read的输入重定向为脚本的第一个参数,也就是数据文件
-
二、控制脚本
2.1 处理信号
-
Linux信号
1
SIGHUP 挂起进程2
SIGINT 终止进程3
SIGQUIT 停止进程9
SIGKILL 无条件终止进程15
SIGTERM 尽可能终止进程17
SIGSTOP 无条件停止进程,但不是终止进程18
SIGTSTP 停止或暂停进程,但不终止进程19
SIGCONT 继续运行停止的进程
-
生成信号
- 中断进程
Ctrl + C
,生成SIGINT信号 - 暂停进程
Ctrl + Z
,生成SIGTSTP信号- 在前台继续运行
fg %作业号
,使用jobs
查看作业号 - 在后台继续运行
bg %作业号
- 在前台继续运行
- 中断进程
-
捕获信号
-
作用:如果脚本收到了trap命令中列出的信号,该信号不再由shell处理,而是交由本地处理。
-
语法
trap 命令 信号
-
例子:
#/bin/bash # 打印100~0 COUNT=100 # 收到SIGINT时,直接打印这句话 trap "echo 'sorry i can continue..'" SIGINT while [ $COUNT -ge 0 ] do echo "current is $COUNT" COUNT=$[$COUNT - 1] sleep 1 done -
捕获脚本退出,也就是脚本退出时,执行操作
trap "echo Goodbye..." EXIT
-
修改信号,只需要重写一遍即可,即捕获相同的信号,后者会覆盖前者。
-
删除信号
trap -- SIGINT
-
2.2 后台运行脚本
- 运行脚本时,在命令末尾加一个
&
,例如sleep 100&
- 然后shell会输出信息
[3] 52261
,方框代表的是作业号,后面的数字是进程号 - 后台作业仍然会使用当前shell的STDIN、STDOUT、STDERR
- 后台进程和当前shell联系在一起,如果当前shell退出,所有的后台进程结束。
- 然后shell会输出信息
- 在非控制台下运行脚本
nohub 脚本 &
- 如果关闭会话,脚本会忽略终端会话发来的SIGHUP信号。
- nohup命令会将STDOUT和STDERR消息重定向到
nohup.out
文件中。
2.3 作业控制
- 查看作业:
jobs
-l
列出进程PID和作业号-r
只列出运行中的作业-s
只列出已停止的作业- 列出的作业有+和-,+被当作默认作业,在使用作业控制命令时,如果未在命令行指定任何作业号,该作业被当作对象。-被当作下一个默认作业。同一时刻只有一个+和一个-
- 重启停止的作业
bg %作业号
后台继续运行fg %作业号
前台继续运行
2.4 调整优先级
- 从+20(最高优先级)到+19(最低优先级)。默认情况下,bash shell以优先级0来启动所有进程。
nice
命令:nice -n 10 脚本
,以10的优先级运行脚本- 如果要调整到0更高的优先级,需要有管理员权限。
- 可以使用
ps -p 进程号 -o ni
来查看进程的优先级
renice
命令:改变系统上已运行命令的优先级renice -n 优先级 -p pid
- 只能对所属的进程执行renice
- 只能通过renice降低进程的优先级
2.5 定时运行作业
at
命令- at命令会将作业提交到队列,at的守护进程atd以后台模式运行,检查作业队列来运行作业。
- atd守护进程会检查系统的一个特殊目录
/var/spool/at
来获取at命令提交的作业,默认情况下,每隔60秒检查一次。 - 格式
at -f 脚本 时间
at
命令利用sendmail
应用程序发送邮件。因此,最好将STDOUT
和STDERR
重定向。atq
列出有哪些作业在等待。atq 作业号
删除作业。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理