AI智能
改变未来

Shell从入门到精通

熟悉基本shell操作不仅是运维的基本功,对于开发来说也是多多益善,我在学习的过程中,总结了十个练手的小demo,并附上涉及的知识点,仅供娱乐。

1. 多线程ping监控,检查同一网段的IP是否连通

  • Linux 系统中有一个特殊的设备/dev/null,这是一个黑洞。无论往该文件中写入多少数据,都会被系统吞噬、丢弃。如果有些输出信息是我们不再需要的, 则可以使用重定向将输出信息导入该设备文件中。注意:数据一旦导入黑洞将无法找回。
  • 重定向: > 是覆盖重定向, >> 是累加重定向
  • $0 这个程式的执行名字
    $n 这个程式的第n个参数值,n=1..9
    $* 这个程式的所有参数,此选项参数可超过9个。
    $# 这个程式的参数个数
    $$ 这个程式的PID(脚本运行的当前进程ID号)
    $! 执行上一个背景指令的PID(后台运行的最后一个进程的进程ID号)
    $? 执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)
    $- 显示shell使用的当前选项,与set命令功能相同
    $@ 跟$*类似,但是可以当作数组用
  • & 表示任务在后台执行,如要在后台运行redis-server,则有redis-server &
    && 表示前一条命令执行成功时,才执行后一条命令 ,如 echo \’1‘ && echo \’2\’
    | 表示管道,上一条命令的输出,作为下一条命令参数,如 echo \’yes\’ | wc -l
    || 表示上一条命令执行失败后,才执行下一条命令,如 cat nofile || echo \”fail\”

  • ping 命令语法

    ping [-dfnqrRv][-c<完成次数>][-i<间隔秒数>][-I<网络界面>][-l<前置载入>][-p<范本样式>][-s<数据包大小>][-t<存活数值>][主机名称或IP地址]
#!/bin/bash#使用&开启后台进程net=\"101.200.35\"mult_ping() {ping -c2 -i0.2 -W1 $1 &>/dev/nullif [ $? -eq 0 ];thenecho \"$1 is up\"elseecho \"$1 is down\"fi}for i in {0..255}domult_ping $net.$i &donewait

2.进度条功能显示

  • 常见系统预设变量
#!/bin/bashtrap \'kill $!\' INT# 定义宽度为50的进度条# 输出完成后将/r光标切换到行首,准备下一次进度条显示bar () {while :pound=\"\"for ((i = 47; i>=1; i-- ))dopound += #printf \"|%s%${i}s|\\r\" \"$pound\"sleep 0.2done}# 调用函数,显示进度符号,直到复制结束kill进度函数bar &cp -r $1 $2kill $!echo \"复制结束\"

3. Linux创建进程的三种方式

  1. fork
    通常情况下在系统中通过相对路径或绝对路径执行一个命令时,都会由父进程开启一个子进程,当子进程结束后再返回父进程,这种行为过程就叫作fork。当脚本中正常调用一个外部命令 1或其他脚本时,都会fork一个子Shell进程,我们的命令会运行在这个子Shell中。
  2. exec
    使用 exec 方式调用其他命令或脚本时,系统不会开启子进程,而是使用新的程序替换当前的 Shell 环境,因为当前 Shell 环境被替换了,所以当 exec 调用的程序结束后,当前环境会被关闭。但是有一个特例,当 exec 后面的参数是文件重定向时,不会替换当前 Shell 环境,脚本后续的其他命令也不会受到任何影响。
  3. source或 . (点)
    使用 source 命令或.(点)可以不开启子 Shell,而在当前 Shell 环境中将需要执行的命令加载进来,执行完加载的命令后,继续执行脚本中后续的指令。

分析工具:pstree 进程树

4. 控制进程数量——文件描述符和命名管道

文件描述符

文件描述符是一个非负整数,而内核需要通过这个文件描述符才可以访问文件。当我们在系统中打开已有的文件或新建文件时,内核每次都会给特定的进程返回一个文件描述符,当进程需要对文件进行读或写操作时,都要依赖这个文件描述符进行。文件描述符就像一本书的目录页数(也叫索引),通过这个索引可以找到需要的内容。在 Linux 或类 UNIX系统中内核默认会为每个进程创建三个标准的文件描述符,分别是 0(标准输入)、 1(标准输出)和 2(标准错误)。通过查看/proc/PID 号/fd/目录下的文件,就可以查看每个进程拥有的所有文件描述符。

创建文件描述符:

exec 文件描述符 <> 文件名

调用文件描述符语法格式:

&文件描述符

关闭文件描述符:

exec 文件描述符<&-exec 文件描述符>&-

命名管道

管道是进程间通信的一种方式,匿名管道,使用|符号就可以创建一个匿名管道,顾名思义,系统会自动创建一个可以读写数据的管道,但是这个管道并没有名称。一个程序往管道中写数据,另一个程序就可以从管道中读取数据。但是匿名管道仅可以实现父进程与子进程之间的数据交换,能不能实现任意两个无关的进程之间的通信呢?答案是肯定的,使用命名管道,也叫FIFO1文件。

命名管道的特征:

  • FIFO 文件由命令创建(mknod 或 mkfifo 命令),可以在文件系统中直接看到。
  • 写入管道的数据一旦被读取后,就不可以再重复读取。
  • 进程往命名管道中写数据时,如果没有其他进程读取数据,则写进程会被阻塞。
  • 进程尝试从命名管道中读取数据时,如果管道中没有数据,则读进程会被阻塞。
  • 命名管道中的数据常驻内存,并不实际写入磁盘,读写效率会更高。

5. 可任意控制进程数量的多线程ping

第一个demo中,通过 & 开启任意数量线程进行ping,但是这里的线程不可控。我们用上面的文件描述符和命名管道的知识,写一段可控的多线程ping。

#!/bin/bashpipefile=/tmp/procs_$$.tempnum=10net=\"101.200.35\"multi_ping() {ping -c2 -i0.2 -W1 $1 &>/dev/nullif [ $? -eq 0 ];thenecho \"$1 is up\"elseecho \"$1 is down\"fi}# 创建命名管道文件,创建其文件描述符,通过重定向将数据导入管道文件mkfifo $pipefileexec 12<>$pipefilefor i in `seq $num`doecho \"\" >&12 &done# 成功读取命名管道中的数据后开启新的进程# 所有内容读取完之后read被阻塞,无法再启动新的进程# 等待前面启动的线程结束后,继续往管道文件中写入数据,释放阻塞,再次开启新的线程for j in {1..254}doread -u12{multi_ping $net.$jecho \"\" >&12} &donewaitrm -rf $pipfile

6. sed爬虫批量下载美女图片

  • sed命令汇总




  • sed 是逐行处理软件,我们可能仅输入了一条 sed 指令,但系统会将该指令应15a8用在所有匹配的数据行上,因此相同的指令会被反复执行 N 次,这取决于匹配到的数据有几行。
  • 默认 sed 不支持扩展正则,如果希望使用扩展正则匹配数据,可以使用-r 参数。
  • sed 程序使用=指令可以显示行号,结合条件匹配,可以显示特定数据行的行号。
  • 在 sed 中支持使用感叹号(!)对匹配的条件进行取反操作。

下载思路:用curl获取网站源代码+sed数据清洗获取图片地址+wget下载保存

#!/bin/bash# 爬取美女图片# 定义要爬取的网站和保存的文件page=\"https://tieba.baidu.com/p/4420470629\"URL=\"beau.txt\"# 将网站源代码保存到文件中curl -s https://tieba.baidu.com/p/4420470629 > $URL# 对源代码数据过滤清洗,获取种子的URL链接echo -e \"\\033[32m 正在获取种子 URL,请稍后...\\033[0m\"sed -i \'/<img/!d\' $URL #删除不包含<img 的行sed -i \'s/.*src=\"//\' $URL #删除 src=\"及其前面的所有内容sed -i \'s/\".*//\' $URL #删除双引号及其后面的所有内容echo#利用循环批量下载所有图片数据#wget 为下载工具,其参数选项描述如下:# -P 指定将数据下载到特定目录(prefix)# -c 支持断点续传(continue)# -q 不显示下载过程(quiet)echo -e \"\\033[32m 正在批量下载种子数据,请稍后...\\033[0m\"for i in $(cat $URL)dowget -P tempPhoto/ -c $idone

这种知识最基本的爬虫,对于反爬虫的网站就嗝屁了,对于那种异步加载的也没办法,总之,就是比较弱。

7. sed随机点名器

做一个互联网大佬的随机点名器

#!/bin/bash#按 Ctrl+C 组合键时:恢复光标,恢复终端属性,清屏,退出脚本#防止程序意外中断导致的终端混乱trap \'tput cnorm;stty $save_property;clear;exit\' 2#定义变量:人员列表文件名,文件的行数,屏幕的行数,屏幕的列数name_file=\"name.txt\"line_file=$(sed -n \'$=\' $name_file)line_screen=`tput lines`column_screen=`tput cols`#设置终端属性save_property=$(stty -g) #保存当前终端所有属性tput civis #关闭光标#随机抽取一个人名(随机点名)while :dotmp=$(sed -n \"$[RANDOM%line_file+1]p\" $name_file)#随机获取文件的某一行人名tput clear #清屏tput cup $[line_screen/4] $[column_screen/4]echo -e \"\\033[3;5H 随机点名器(按 P 停止): \"echo -e \"\\033[4;5H#############################\"echo -e \"\\033[5;5H# #\"echo -e \"\\033[6;5H#\\t\\t$tmp\\t\\t#\"echo -e \"\\033[7;5H# #\"echo -e \"\\033[8;5H#############################\"sleep 0.1stty -echoread -n1 -t0.1 inputif [[ $input == \"p\" || $input == \"P\" ]];thenbreakfidonetput cnorm #恢复光标stty $save_property #恢复终端属性

8.系统性能监控脚本

  • awk语法格式
  • awk变量
  • awk条件匹配
  • awk 可以通过-v(variable) 选项设置或者修改变量的值,我们可以使用-v 定义新的变量,也可以使用该选项修改内置变量的值。
  • 使用[]定义分隔符集合,同时设置多个分隔符。比如使用[:,-]表示以冒号(:)、逗号(,)或者横线(-)为分隔符
  • [-F|-f|-v] 大参数,-F指定分隔符,-f调用脚本,-v定义变量 var=value

9.监控网络连接状态

  • ss语法格式
#!/bin/bash# 监控网络连接状态#所有 TCP 连接的个数TCP_Total=$(ss -s | awk \'$1==\"TCP\"{print $2}\')#所有 UDP2084连接的个数UDP_Total=$(ss -s | awk \'$1==\"UDP\"{print $2}\')#所有 UNIX sockets 连接个数Unix_sockets_Total=$(ss -ax | awk \'BEGIN{count=0} {count++} END{printcount}\')#所有处于 Listen 监听状态的 TCP 端口个数TCP_Listen_Total=$(ss -antlpH | awk \'BEGIN{count=0} {count++} END{printcount}\')#所有处于 ESTABLISHED 状态的 TCP 连接个数TCP_Estab_Total=$(ss -antpH | awk \'BEGIN{count=0} /^ESTAB/{count++}END{print count}\')#所有处于 SYN-RECV 状态的 TCP 连接个数TCP_SYN_RECV_Total=$(ss -antpH | awk \'BEGIN{count=0} /^SYN-RECV/{count++}END{print count}\')#所有处于 TIME-WAIT 状态的 TCP 连接个数TCP_TIME_WAIT_Total=$(ss -antpH | awk \'BEGIN{count=0} /^TIME-WAIT/{count++}END{print count}\')#所有处于 TIME-WAIT1 状态的 TCP 连接个数TCP_TIME_WAIT1_Total=$(ss -antpH | awk \'BEGIN{count=0}/^TIME-WAIT1/{count++} END{print count}\')#所有处于 TIME-WAIT2 状态的 TCP 连接个数TCP_TIME_WAIT2_Total=$(ss -antpH | awk \'BEGIN{count=0}/^TIME-WAIT2/{count++} END{print count}\')#所有远程主机的 TCP 连接次数TCP_Remote_Count=$(ss -antH | awk \'$1!~/LISTEN/{IP[$5]++} END{ for(i inIP){print IP[i],i} }\' | sort -nr)#每个端口被访问的次数TCP_Port_Count=$(ss -antH | sed -r \'s/ +/ /g\' | awk -F\"[ :]\"\'$1!~/LISTEN/{port[$5]++} END{for(i in port){print port[i],i}}\' | sort -nr)#定义输出颜色SUCCESS=\"echo -en \\\\033[1;32m\" #绿色NORMAL=\"echo -en \\\\033[0;39m\" #黑色#显示 TCP 连接总数tcp_total(){echo -n \"TCP 连接总数: \"$SUCCESSecho \"$TCP_Total\"$NORMAL}#显示处于 LISTEN 状态的 TCP 端口个数tcp_listen(){echo -n \"处于 LISTEN 状态的 TCP 端口个数: \"$SUCCESSecho \"$TCP_Listen_Total\"$NORMAL}#显示处于 ESTABLISHED 状态的 TCP 连接个数tcp_estab(){echo -n \"处于 ESTAB 状态的 TCP 连接个数: \"$SUCCESSecho \"$TCP_Estab_Total\"$NORMAL}#显示处于 SYN-RECV 状态的 TCP 连接个数tcp_syn_recv(){echo -n \"处于 SYN-RECV 状态的 TCP 连接个数: \"$SUCCESSecho \"$TCP_SYN_RECV_Total\"$NORMAL}#显示处于 TIME-WAIT 状态的 TCP 连接个数tcp_time_wait(){echo -n \"处于 TIME-WAIT1 状态的 TCP 连接个数: \"$SUCCESSecho \"$TCP_TIME_WAIT1_Total\"$NORMAL}#显示处于 TIME-WAIT2 状态的 TCP 连接个数tcp_time_wait2(){echo -n \"处于 TIME-WAIT2 状态的 TCP 连接个数: \"$SUCCESSecho \"$TCP_TIME_WAIT2_Total\"$NORMAL}#显示 UDP 连接总数udp_total(){echo -n \"UDP 连接总数: \"$SUCCESSecho \"$UDP_Total\"$NORMAL}#显示 UNIX sockets 连接总数unix_total(){echo -n \"Unix sockets 连接总数: \"$SUCCESSecho \"$Unix_sockets_Total\"$NORMAL}#显示每个远程主机的访问次数remote_count(){echo \"每个远程主机与本机的并发连接数: \"$SUCCESSecho \"$TCP_Remote_Count\"$NORMAL}#显示每个端口的并发连接数port_count(){echo \"每个端口的并发连接数: \"$SUCCESSecho \"$TCP_Port_Count\"$NORMAL}print_info(){echo -e \"------------------------------------------------------\"$1}print_info tcp_totalprint_info tcp_listenprint_info tcp_estabprint_info tcp_syn_recvprint_info tcp_time_waitprint_info tcp_time_wait1print_info tcp_time_wait2print_info udp_totalprint_info unix_totalprint_info remote_countprint_info port_countecho -e \"------------------------------------------------------\"

参考

  • http://www.dtmao.cc/news_show_650108.shtml
  • http://www.qishunwang.net/news_show_11182.aspx
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Shell从入门到精通