编写可靠Linux shell脚本的八个建议

这八个建议,来源于键者几年来编写 shell 脚本的一些经验和教训。事实上开始写的时候还不止这几条,后来思索再三,去掉几条无关痛痒的,最后剩下八条。毫不夸张地说,每条都是精挑细选的,虽然有几点算是老生常谈了。1. 指定bashshell 脚本的第一行,#!之后应该是什么?如果拿这个问题去问别人,不同的人的回答可能各不相同。我见过/usr/bin/env bash,也见过/bin/bash,还有/usr/bin/bash,还有/bin/sh,还有/usr/bin/env sh。这算是编程界的“’茴’字四种写法”了。在多数情况下,以上五种写法都是等价的。但是,写过程序的人都知道:“少数情况”里往往隐藏着意想不到的坑。如果系统的默认 shell 不是 bash 怎么办?比如某 Linux 发行版的某个版本,默认的 sh 就不是 bash。如果系统的 bash 不是在 /usr/bin/bash 怎么办?我推荐使用 /usr/bin/env bash 和 /bin/bash。前者通过env添加一个中间层,让env在$PATH中搜索bash;后者则是官方背书的,约定俗成的 bash 位置,/usr/bin/bash不过是指向它的一个符号链接。2. set -e 和 set -xOK,经过一番讨论,现在第一行定下来了。接下来该开始写第二行了吧?且慢!在你开始构思并写下具体的代码逻辑之前,先插入一行set -e和一行set -x。set -x会在执行每一行 shell 脚本时,把执行的内容输出来。它可以让你看到当前执行的情况,里面涉及的变量也会被替换成实际的值。set -e会在执行出错时结束程序,就像其他语言中的“抛出异常”一样。(准确说,不是所有出错的时候都会结束程序,见下面的注)注:set -e结束程序的条件比较复杂,在man bash里面,足足用了一段话描述各种情景。大多数执行都会在出错时退出,除非 shell 命令位于以下情况:一个 pipeline 的非结尾部分,比如  error | ok一个组合语句的非结尾部分,比如  ok && error || other一连串语句的非结尾部分,比如  error; ok位于判断语句内,包括 test、if、 while 等等。这两个组合在一起用,可以在 debug 的时候替你节省许多时间。出于防御性编程的考虑,有必要在写第一行具体的代码之前就插入它们。扪心自问,写代码的时候能够一次写对的次数有多少?大多数代码,在提交之前,通常都经历过反复调试修改的过程。与其在焦头烂额之际才引入这两个配置,不如一开始就给 debug 留下余地。在代码终于可以提交之后,再考虑是否保留它们也不迟。3. 带上shellcheck好了,现在我已经有了三行(样板)代码,具体的业务逻辑一行都没写呢。是不是该开始写了?且慢!工欲善其事,必先利其器。这次,我就介绍一个 shell 脚本编写神器:shellcheck说来惭愧,虽然写了几年 shell 脚本,有些语法我还是记不清楚。这时候就要依仗 shellcheck 指点一下了。shellcheck 除了可以提醒语法问题以外,还能检查出 shell 脚本编写常见的 bad code。本来我的N条建议里面,还有几条是关于这些 bad code 的,不过考虑到 shellcheck 完全可以发掘出这些问题,于是忍痛把它们都剔除在外了。毫无疑问,使用 shellcheck 给我的 shell 编写技能带来了巨大的飞跃。所谓“站在巨人的肩膀上”,虽然我们这些新兵蛋子,技能不如老兵们强,但是我们可以在装备上赶上对方啊!动动手安装一下,就能结识一个循循善诱的“老师”,何乐而不为?顺便一提,shellcheck 居然是用 haskell 写的。谁说 haskell 只能用来装逼?4. 变量展开在 shell 脚本中,偶尔可以看到这样的做法:echo $xxx | awk/sed/grep/cut...。看起来大张形势的样子,其实不过是想修改一个变量的值。杀鸡何必用牛刀?bash 内建的变量展开机制已经足以满足你各种需求!还是老方法, read the f**k manaul! man bash 然后搜索Parameter...
阅读全文
Shell

https nginx sha256 SSL证书一键制作脚本

密钥长度:2048 加密方式:sha256 域名地址:支持自定义 其它信息:证书信息需要自行填写 代码如下: #!/bin/sh # create self-signed server certificate: read -p "Enter your domain : " DOMAIN echo "Create server key..." #openssl genrsa -des3 -out $DOMAIN.key 2048 openssl genrsa -out $DOMAIN.key 2048 echo "Create server certificate signing request..." SUBJECT="/C=US/ST=Mars/L=iTranswarp/O=iTranswarp/OU=iTranswarp/CN=$DOMAIN" #openssl req -new -sha256 -subj $SUBJECT -key $DOMAIN.key -out $DOMAIN.csr openssl req -out $DOMAIN.csr -key $DOMAIN.key -new -sha256 echo "Remove password..." mv $DOMAIN.key $DOMAIN.origin.key openssl rsa -in $DOMAIN.origin.key -out $DOMAIN.key echo "Sign SSL certificate..." openssl x509 -req -sha256 -days 3650 -in $DOMAIN.csr -signkey $DOMAIN.key -out $DOMAIN.crt echo "TODO:" echo "Copy $DOMAIN.crt to /etc/nginx/ssl/$DOMAIN.crt" echo "Copy $DOMAIN.key to /etc/nginx/ssl/$DOMAIN.key" echo...
阅读全文

Linux apache日志分析常用命令汇总

以下命令全部基于apache默认的日志格式 1、查看当天有多少个IP访问: awk '{print $1}' log_file|sort|uniq|wc –l 2、查看某一个页面被访问的次数: grep "/index.php" log_file | wc –l 3、查看每一个IP访问了多少个页面: awk '{++S} END {for (a in S) print a,S}' log_file 4、将每个IP访问的页面数进行从小到大排序: awk '{++S} END {for (a in S) print S,a}' log_file | sort –n 5、查看某一个IP访问了哪些页面: grep ^111.111.111.111 log_file| awk '{print $1,$7}' 6、去掉搜索引擎统计当天的页面: awk '{print $12,$1}' log_file | grep ^\"Mozilla | awk '{print $2}' |sort | uniq | wc –l 7、查看2009年6月21日14时这一个小时内有多少IP访问: awk '{print $4,$1}' log_file | grep 21/Jun/2009:14 | awk '{print $2}'| sort | uniq | wc –l 8.查看访问前十个ip地址 awk '{print $1}' |sort|uniq -c|sort -nr |head -10 log_file 9.访问次数最多的文件或页面 cat log_file |awk '{print $11}'|sort|uniq -c|sort -nr...
阅读全文

shell环境变量以及set,env,export的区别

一.shell环境变量的分类以及set env export的区别: set:显示(设置)shell变量 包括的私有变量以及用户变量,不同类的shell有不同的私有变量 bash,ksh,csh每中shell私有变量都不一样 env:显示(设置)用户变量变量 export:显示(设置)当前导出成用户变量的shell变量。 举个例子来讲:    view plaincopy   $ aaa=bbb --shell变量设定      $ echo $aaa       bbb      $ env| grep aaa --设置完当前用户变量并没有      $ set| grep aaa  --shell变量有      aaa=bbb      $ export| grep aaa --这个指的export也没导出,导出变量也没有      $ export aaa   --那么用export 导出一下      $ env| grep aaa  --发现用户变量内存在了      aaa=bbb   总结:linux 分 shell变量(set),用户变量(env), shell变量包含用户变量,export是一种命令工具,是显示那些通过export命令把shell变量中包含的用户变量导入给用户变量的那些变量. 二:使用unset命令来清除环境变量,注意set env  export设置的变量,都可以用unset来清除的 view plaincop   清除环境变量的值用unset命令。如果未指定值,则该变量值将被 设为NULL。示   例如下:     $ export TEST="Test..." #增加一个环境变量TEST     $ env|grep TEST #此命令有输入,证明环境变量TEST已经存在了     TEST=Test...     $ unset $TEST #删除环境变量TEST     $ env|grep TEST #此命令没有输出,证明环境变量TEST已经不存在了     三:使用readonly命令设置只读变量  view plaincopy   使用了readonly命令的话,变量就不可以被修改或清除了。示例如下:   $ export TEST="Test..." #增加一个环境变量TEST   $ readonly TEST #将环境变量TEST设为只读   $ unset TEST #会发现此变量不能被删除   -bash: unset: TEST: cannot unset: readonly variable   $ TEST="New" #会发现此也变量不能被修改   -bash: TEST: readonly variable     四:最根本的设置、更改变量的配置文件 ~/.bash_profile   ~/.bashrc   ~/.bash_logout ~/.bash_profile  用户登录时被读取,其中包含的命令被执行 ~/.bashrc  启动新的shell时被读取,并执行 ~/.bash_logout  shell 登录退出时被读取 此外,shell(这里指bash)的初始化过程是这样的: 1.bash 检查文件/etc/profile 是否存在 2. 如果存在,bash 就读取该文件,否则,跳过 3.bash 检查主目录下的文件.bash_profile 是否存在。 4. 如果存在,bash 就读取該文件,否则,跳过 5.bash 检查主目录下的.bash_login 是否存在。 6. 如果存在,bash 就读取该文件,否则,跳过 7.bash 检查主目录下的文件.profile 是否存在 8. 如果存在, bash 就读取该文件,否则,跳过。 这些步骤都执行完后,就出现提示符了, ksh 默认提示符是 $. 五:常见的shell变量 PATH 这个变量包含了一系列由冒号分隔开的目录,系统就从这些目录里寻找可执行文件。如果你输入的可执行文件(例如ls、rc-update或者emerge) 不在这些目录中,系统就无法执行它(除非你输入这个命令的完整路径,如/bin/ls)。   ROOTPATH 这个变量的功能和PATH相同,但它只罗列出超级用户(root)键入命令时所需检查的目录。   LDPATH 这个变量包含了一系列用冒号隔开的目录,动态链接器将在这些目录里查找库文件。  MANPATH 这个变量包含了一系列用冒号隔开的目录,命令man会在这些目录里搜索man页面。   INFODIR 这个变量包含了一系列用冒号隔开的目录,命令info将在这些目录里搜索info页面。   PAGER 这个变量包含了浏览文件内容的程序的路径(例如less或者more)。   EDITOR 这个变量包含了修改文件内容的程序(文件编辑器)的路径(比如nano或者vi)。   KDEDIRS 这个变量包含了一系列用冒号隔开的目录,里面放的是KDE相关的资料。  CONFIG_PROTECT 这个变量包含了一系列用空格隔开的目录,它们在更新的时候会被Portage保护起来。  CONFIG_PROTECT_MASK 这个变量包含了一系列用空格隔开的目录,它们在更新的时候不会被Portage保护起来。 PATH:决定了shell将到哪些目录中寻找命令或程序 HOME:当前用户主目录 MAIL:是指当前用户的邮件存放目录。 SHELL:是指当前用户用的是哪种Shell。 HISTSIZE:是指保存历史命令记录的条数...
阅读全文