How to write an Elegant Shell Script,怎样写一段优雅的 Shell 脚本

从一段简单的备份脚本说起, We will start from a simple backup script

这个脚本的目的是把一个 Web 目录用 tar cjf 打包,然后可以由不同的用户执行,不会有文件读写权限问题,每次打包前,要把之前的打包文件添加上日期备份好,如果打包失败,还要恢复。 The purpose of the demo script is to use “tar cjf” command to backup a web folder, different user can execute this script and will have no file permission issue. The original tarball file should be backup first to a new file name with date in the filename. In the case of failure, we will roll back the backup file.

我们会备份不同的 Web 目录,所以需要命令行传入参数,我们使用了 case 语句来判断,之所以没有用 if 是因为 if 语句会产生大量的 else 子句导致程序的可读性很差。 We will use this scrpt to backup different web folder, so the folder type is used as parameter to pass into the script. But we did not use if statement, because it will need lots “else” which will cause the script less readable. We use “case” statement to parse the “APP” name in this script.

为了记录是谁做的备份,我们需要写日志文件,而为了记录日志文件,我们写一个简单的 logger 函数,以参数传入的方式,把 logger 之后的参数 echo 显示,并添加到日志文件。 To record who and when, the user run the script, we write a function “logger”, which will need one “MSG” parameter, to echo and to append to the log file.

为了优雅的记录所有的日志,我们在日志的 MSG 前都添加了日志的 level, [debug], [info], [error], [warning] 等,我们之后如果需要分析日志,就可以简单的通过 grep 这些日志级别的关键字来查找。不同的用户可能在自己的环境会使用不同的语言设置 LC_ALL 变量,为了统一用英文,在脚本里,我们强行设置了 LC_ALL=”C”。 To record all the running log, we add log level verbosity in every MSG, e.g. [debug],[info],[error],[warning]. In case we need to find something in the log, we can just use these keywords to grep. Different user may have different language preference in their environment, to force the log file use all in English, we add LC_ALL=”C”.

为了记录所有可能的错误,我们在一些关键的执行命令后都添加了 “2>> $LOG” 这样的语句,这是为了记录可能的错误都写入到日志里。 To capture any ciritical error message, we add “2 >>$LOG” in every critial running command, so all the error log will be append to LOG file as well.

考虑到可能多人执行脚本,生成的文件权限会存在问题,我们用 umask 强制 owner 和 group 可写,同时把成功备份后的文件,强制设置为一个 同一个组 的组名。 Considered on the different user may execute the srcript, we have to make the file owner and group writeable(umask 002), and in the final step, in the success of the backup, we will change the target file’s group to a particular one, not the primary group of the executor.

好了,就说这么多吧,上全文
OK, That’s it, let just view the whole script.

Linux 每日一题 20190613: cut & paste

我们经常说 Copy/Paste, 互联网上的内容就是一个拷贝,粘贴。在 Linux 里面,copy 是 cp 命令, 是用来拷贝文件的。 Mac 里面有个命令可以把数据真的复制到内存,我忘记是什么命令了。 今天讲 cut 和 paste。 cut 命令也不是 Copy/Paste 里的 剪切, 把内容删除掉,并把内容丢进内存缓冲区。

Linux 里的 cut 命令就是把文件的一部分取出来。
cut -c1-10 /etc/hosts 就是把 hosts 文件里每行的第一个到第10个字符取出来
cut -d ‘ ‘ -f1 /etc/hosts 就是以空格为分隔符,把第一个字段取出来
# grep ^192 /etc/hosts|cut -d ‘ ‘ -f1|cut -d ‘.’ -f1-4 –output-delimiter “|” 可以把 hosts 文件的 192 开头的 IP 地址, 小数点替换成管道符号。

可以看到 cut 命令在处理比较简单的逻辑时,是非常容易的。

下面来讲 paste 。paste 可以把两个文件以列的形式合并起来,例子:

我们可以用 减号 替换 输入的某个字段来打印输出指定的字段合并,例子:

这个意思是说,两个减号的位置从后面输入流文件里依次读入

总结: cut 用的人比较多, paste 用的人比较少,但是在某些情形下很有用。

Linux 每日一题 20190612: tee

昨天建了一个 QQ 群,号称“Linux 高级群” 群号 816821779
今早心血来潮,开始给群友们普及 Linux 知识,早上泡茶,想到了 tee 这个命令。

其实 不需要百度/Google, Linux 知识里,最好的就是 man。
man tee 告诉我们运行 info coreutils ‘tee invocation’
于是我们可以看到其中的例子:

wget -O - http://example.com/dvd.iso \
   | tee >(sha1sum > dvd.sha1) > dvd.iso

上面其实就是把网站上的 dvd 片子发送到 STDOUT,同时计算 sha1sum 值,又把整个 iso 文件写到本地

再来看另外一个目录压缩为两种不同 压缩格式文件的方式. 传统的一个目录备份, 要分别压缩成 .tar.gz 和 .tar.bz2 的话, 要做两次 tar chof :
tar chof – “$tardir” | gzip -9 -c > pkg.file.tar.gz
tar chof – “$tardir” |bzip2 -9 -c > pkg.file.tar.bz2
用 tee 命令,一次解决:
tar chof – “$tardir” | tee > (gzip -9 -c > pkg.tar.gz) \
|bzip2 -9 -c > pkg.tar.bz2

扩展知识:在 MySQL/Oracle 等 SQL 命令行工具下, 我们也会用到把命令窗口的内容发送到操作系统文件的做法。以 MySQL 为例, 就是 \T, 或者直接 tee
停止发送 用 \t 关闭

验证命令输出已经写到指定的文件:

加群 二维码: