shell应用分享
简介
本文目的
是什么(what) 有什么好(why) 用武之地(where) 怎么学/用(how)- 简单介绍shell,对比几种常见shell,并推荐bash作为您工作学习的shell
- 如何得到shell帮助
- 快速用shell进行编程
- 如何提高shell能力
- shell应用案例分享
本文将不介绍以下内容,但您可以通过google很容易找到相关内容
- 如何安装shell
shell简介
本章回答前三个问题(3W)
shell是一个解释器(what)
shell是linux/unix及其他类unix系统的下的解释器, 它提供了用户与系统交互的接口
shell本质是一个linux进程,演示ps shellshell的种类
- sh: Bourne Shell , 伯恩shell , 比较原始的shell, 极其精简,基本上所有类unix系统都有安装
- bash: Bourne Again Shell , 伯恩shell加强版 , 兼容sh, 命令补全,文档丰富,社区活跃.
- ksh: korn Shell , 科恩shell , 语法元素较丰富,文档较少,学习成本高
- csh: ‘C’ Shell , 提供了与C语言相近的语法元素,比如
if ( expr )
, 并且还要尽量兼容sh,因此语法怪异 - tcsh: 新一代 csh , 兼容csh , 提供命令补全
shell推荐
参考:shell比较
Shell Experience Editing Shortcuts Portability Learning Bourne 3 3 3 1 1 POSIX 2 1 2 1 2 C 2 2 2 3 2 Korn 1 1 2 2 2 TC 2 1 1 3 3 Bourne Again 2 1 1 2 3 Z 2 1 1 3 3 shell的优势(why)
- 简单的输入输出模式
- 管道
- 通用性强
- 程序重用
shell干什么用(where)
- 启停程序(调度)
- 查看系统状态
- 日志分析
- 数据处理
- 上传下载
- 监控及调度
- 调试测试
- ……(太多,列不完)
- 以上所有内容的组合
shell不适合干什么
作为应用场景的补充,这里说下哪些场景不适合用shell实现
- 计算密集型任务
- 高性能服务场景
- 复杂数据结构
shell怎么用(how)
shell本质就是命令的堆砌
shell逐行执行如下代码:
命令 [参数]
如:
ls -lart
cd /data
which ls
命令可以是shell内置的,也可以是外部程序
内置命令如: cd , alias , source , jobs如何获得帮助
shell本身帮助
man bash
程序帮助
man <程序名>
搜索帮助
apropos 程序名|程序名的一部分
阅读代码时不熟的命令或程序
先apropos查看该命令或程序是否存在,不存在可能为命令,可在man bash
里搜索借助搜索引擎
google
shell语法提要
- 控制
- 循环
- 变量替换(Parameter Expansion)
- 输入输出
- 管道
- 表达式执行(eval)
- 函数
shell语法
控制结构
if test 1 -eq 2 ; then echo "true" ; fi if [ 1 -eq 2 ] ; then echo "true" ; fi
其中test有两种表示方式,第二种比较常用。第二种需要注意
[]
前后都需要有空格。循环结构
- while 循环
while [ expr ] ; do <commands> done
- foreach循环
for i in a b c do echo $i <commands> done
- for 循环(类C)
for (( i=0 ; i<3; i++ )) do echo $i <commands> done
- case
i=1 case $i in 1) <commands> break;; *2) <commands> break;; *) <commands> esac
*
是通配符,指代任意字符组合变量替换
也称为参数扩展,可以在man bash
页中查找Parameter Expansion
s="a/b%c#d" # 截头 echo ${s#*/} # 去尾 echo ${s%#*} echo ${s%\%*}
%
,#
成双时,表示贪婪模式
\
是转义字符, 当一个字符会产生歧义时,shell优先会将其当成控制字符,而非打印字符,用转义字符将之转为打印字符输入输出
- 输出到tty(stdout)
echo "yy" cat file.txt
- 输出重定向
echo "yy" > file.tmp cat file.txt >> file.tmp
- 输入(stdin)
while read token do echo $token done
- 输入重定向
cat < file.tmp while read token do echo $token done < file.tmp
管道
将上一级命令的stdout绑定到下一级命令的stdin, 这就是管道。ls|grep txt$ cat | awk '{print $2}'
表达式执行(eval)
将一个文本当成命令执行,并获得其输出,就是表达式执行(eval)或称为表达式求值。
有两种形式:- 由
``
引起来,如:
sum=`expr 1 + 2` echo $sum lastlog=`tail -1 some.log` echo $lastlog
- 由
$()
引起来,如:
pronum=$(ps -ef|grep nginx|grep -v grep|wc -l) echo $pronum
- 由
$(())
引起来,这种表达式必须是数值计算的表达式如:
mul = $(( 3 * 4 )) pow = $(( 2 ** 5 )) echo $mul $pow
- 由
自定义函数
自定义函数可以当命令使用,如
proc_search() { ps -ef|grep $1|grep -v grep } proc_search nginx
shell进阶 - 常用工具软件
shell大部分时候都要借助于工具来实现复杂功能。同时用shell来调用也能使软件实现更专业化,简单化
- 计算器: bc
- JSON: jq , js-beautify
- 文件处理: grep ,awk , sed ,perl
- 二进制处理: xxd , od
- 二进制烧录: dd
- 网络调试: nc, tcpdump ,wireshark, socat
执行脚本
写完脚本,需要执行,脚本才会变的有意义,就如同赋予脚本生命。- 给脚本执行权限
chmod +x ./script.sh
- 执行
./script.sh
或bash ./script.sh
- 对于
./script.sh
这种执行方式, shell在调度时会查看被称为Shebang
的行, 即脚本开头的#!/bin/sh
, 告知(解释器)整体对待该文件的方式
- 给脚本执行权限
环境变量
这是脚本执行的另一个重要概念,环境变量会影响进程并传递给其子进程。如:
LIBRARY_PATH
LD_PRELOAD
shell应用场景及案例
掌握了shell的语法,接下来可以开始实战了
do one thing and do it well – Bash 的威力
批量重命名
我们目录里有多个mp3文件,但其名字不够规范,我们想进行批量重命名
执行该任务前,我们应该知道,mp3文件有tag信息,这些信息称为ID3, 通过搜索,我们知道id3tool这个工具可帮助我们从mp3的tag信息中获得唱片及演唱者信息。因此我们先安装下该软件apt-get install id3tool
或yum install id3tool
rm -Rf rename_dir mkdir rename_dir cd rename_dir cp ../*.mp3 . for i in *.mp3 do title=`id3tool $i|grep -i title|awk -F: '{gsub(/\s/, "", $2);print $2}'` artist=`id3tool $i|grep -i artist|awk -F: '{gsub(/\s/, "", $2);print $2}'` mv $i "$artist - $title.mp3" done
环境构建
为某个任务构建执行环境,解放双手,也方便新人上手
如先进行数据库操作,然后调用客户端请求,再从数据库查询,检查服务是否正确更新数据
示例:
本例所处环境: 192.168.1.15:10081建立了一个web服务,并可响应host_list请求,并返回数据库中己创建的主机信息。 本例通过修改数据库,并使用curl工具进行测试修改是否生效。mysql -h192.168.1.12 -uroot -proot frame -A --default-character-set=utf8 <<END update t_host set memo = "我们在测试" where host_id = 1 ; END cat > req.json <<END req={"head":{"cmd":3005,"xid":"99902565-22ad-4206-9a86-87af7ee27d4f"},"row_count":10,"offset":0} END urlbase="http://192.168.1.15:10081" reqpath="internal.host_list" ContentType="Content-Type: application/x-www-form-urlencoded" curl -vvv -H "$ContentType" -d @"req.json" $urlbase/$reqpath
调试测试
本例以docker启动一个memcache作为例子,演示如何通过nc工具,进行测试tcp服务。docker run -d --name memcached \ -p 11311:11211/tcp \ memcached:1.5.9-alpine \ memcached -m 64 printf "set mykey 0 60 4\r\ndata\r\n" | nc 192.168.1.15 11311 printf "get mykey\r\n" |nc 192.168.1.15 11311
屏蔽复杂性
当你拿到一个抓包结果文件130.pcap
要分析丢包情况,过去你是怎么做的?在wireshark里肉眼看?还是对着libpcap的开发文档写一个专用的程序(你可能要写filter,dissector,然后还要做数据聚合)?
以上任一种方法估计今天的任务,明天未必能完成。
这里给出一个非常方便的套路,你甚至都不用了解pcap的结构。
130.pcap
是我们在130机器上抓的syn包,以下脚本用来分析syn丢包情况:pcap_extract_syn_ack(){ # 参数1: pcap 文件 # 输出为: 1532536078.101 0 # 1532536078.509 1 tshark -r $1 \ # 过滤器: syn 和 syn ack包 -2R "tcp.flags.syn != 0" \ # 指定输出域 -T fields -e 'frame.time_epoch' -e 'tcp.flags.ack' } join_syn_ack_filter() { # 管道过滤器 # 输入为: 1532536078.101 0 # 1532536078.509 1 # 输出为: 1532536078.101|0|1532536078.509|1 awk '{ if($2==0) { rtrim; printf "\n%s|%s",$1,$2} else{ printf "|%s|%s\n",$1,$2} }' } statistic_syn_ack_filter() { # 输入为: 1532536078.101|0|1532536078.509|1 # 或: 1532536079.101|0 # 或: |1532536079.509|1 # 输出为: 按分钟统计的未匹配结果 awk -F| 'NF==2{ sum[$1 - $1 % 60] ++ }END{ for (i in sum) { print strftime("%Y-%m-%d %H:%M:00",i) ,"\t",sum[i] } }' } pcap_extract_syn_ack 130.pcap |\ join_syn_ack_filter |\ statistic_syn_ack_filter
借助于强大的管道,可以让程序只专注于一个领域的功能, 复杂功能就靠这样组合出来。 借助tshark输出指定字段,还有两级管道。可以看到每个部分只处理极小且简单的任务。
shell cgi
编写脚本
/usr/lib/cgi-bin/env.cgi
#!/bin/bash echo -e "Content-type: text/html\n\n" /bin/cat << EOM <HTML> <HEAD> <TITLE>env</TITLE> </HEAD> <BODY bgcolor="#cccccc" text="#000000"> <P> <SMALL> <PRE> EOM /usr/bin/env /bin/cat << EOM </PRE> </SMALL> </P> </BODY> </HTML> EOM
docker run -it -p 8001:80/tcp debian:jessie-20180625 bash
在docker命令行下执行以下代码:cat > /etc/apt/sources.list <<END deb http://mirrors.163.com/debian stretch main deb http://mirrors.163.com/debian stretch-updates main deb http://mirrors.163.com/debian jessie main contrib non-free deb http://mirrors.163.com/debian jessie-updates main END cat >> /etc/apt/apt.conf.d/01proxy <<END Acquire::HTTP::Proxy "http://192.168.1.15:3142"; Acquire::HTTPS::Proxy "false"; END apt-get update && apt-get install vim-tiny apache2 a2enmod cgi touch /usr/lib/cgi-bin/env.cgi chmod +x /usr/lib/cgi-bin/env.cgi chown www-data /usr/lib/cgi-bin/env.cgi service apache2 restart
可以访问了:
curl http://localhost:8001/cgi-bin/env.cgi
监视文件变更
监视目录变更,并同步更新到另一台主机
#!/sbin/bash host=192.168.1.53 src=/data/admin dst=/data/admin user=root export PATH=$PATH:/usr/local/inotify-3.20.1/bin if [ ! -e "$src" ] \ ||[ ! -e "${inotify_home}/bin/inotifywait" ] \ ||[ ! -e "/usr/bin/rsync" ]; then echo "Check File and Folder" exit 9 fi inotifywait -mrq -e close_write,delete,create,attrib $src | while read files; do cd $src && /usr/bin/rsync -vrtapogL -R --delete ./ --timeout=100 $user@$host01:$dst done
推荐阅读
鸣谢
感谢 pyclear , zhangguanzhang 为本文提供案例