摘自 shell脚本实战 第二版 第二章 改进用户命令

脚本14 格式化过长的行

要是够幸运的话,你所用的 Unix 系统中已经包含了 fmt 命令,如果你日常从事文本工作, 那么这个命令非常有用。不管是格式化电子邮件,还是让文本行占满文档的可用页宽,fmt 都可 助你一臂之力。

但有些 Unix 系统中并没有 fmt。对于遗留系统而言,尤为如此,就算有,通常也只是一种相 当精简的实现。

如代码清单 2-2 所示,在短小的 shell 脚本中可以用 nroff 命令来实现长行自动折行和短行填 充,该命令从一开始就是 Unix 的组成部分,它本身也是一个 shell 脚本包装器。

代码 fmt

#!/bin/bash# fmt -- 文本格式化使用工具,可用作nroff的包容器
# 加入两个使用选项:-w X,指定行宽; -h 允许连字符while getopts "hw:" opt;do # : 表示w 参数后面接收一个值case $opt inh ) hyph=1              ;;w ) width="$OPTARG"     ;;esac
doneshift $(($OPTIND -1))nroff << EOF
.ll ${width:-72}
.na
.hy ${hyph:-0}
.pl 1
$(cat "$@")
EOFexit

运行结果

$ fmt -h -w 50 014-ragged.txt
So she sat on, with closed eyes, and half believed
herself in Wonderland, though she knew she had but
to open them again, and all would change to dull
reality--the grass would be only rustling in the
wind, and the pool rippling to the waving of the
reeds--the rattling teacups would change to tin-
kling sheep-bells, and the Queen's shrill cries
to the voice of the shepherd boy--and the sneeze
of the baby, the shriek of the Gryphon, and all
the other queer noises, would change (she knew) to
the confused clamour of the busy farm-yard--while
the lowing of the cattle in the distance would
take the place of the Mock Turtle's heavy sobs.$ fmt 014-ragged.txt
So she sat on, with closed eyes, and half believed herself in
Wonderland, though she knew she had but to open them again, and all
would change to dull reality--the grass would be only rustling in the
wind, and the pool rippling to the waving of the reeds--the rattling
teacups would change to tinkling sheep-bells, and the Queen's shrill
cries to the voice of the shepherd boy--and the sneeze of the baby, the
shriek of the Gryphon, and all the other queer noises, would change (she
knew) to the confused clamour of the busy farm-yard--while the lowing of
the cattle in the distance would take the place of the Mock Turtle's heavy sobs.

脚本15 删除文件时做备份

Unix 用户最常见的问题之一就是没办法轻易恢复被误删的文件或目录。 这里既没有像 Undelete 360、WinUndelete 那样对用户友好的应用程序,也没有像 OS X 中那样能够一键式轻松 浏览并恢复被删除文件的实用工具。只要输入 rm filename 后按下了回车键,文件就算是没了。

一种解决方法是悄无声息地自动将被删除的文件和目录制作成.deleted-files 归档。利用脚本 中的一些妙招(如代码清单 2-5 所示),用户几乎完全察觉不到这个过程。

代码 newrm

#!/bin/bash# newrm -- 现有rm命令的替代命令
# 该脚本通过用户主目录中创建一个新目录实现了基本的文件回复功能
# 它既可以恢复目录,也可以恢复单个文件。如果用户指定了-f选项,则不归档被删除的文件# 重要的警告:你需要安排cron作业或执行类似清楚归档目录。否则
# 系统并不会真正删除任何文件,这将造成磁盘空间不足archivedir="/.deleted_files"
realrm="$(which rm)"
copy="$(which cp) -R"
if [ $# -eq 0 ];then            # 由rm命令输出用法错误信息exec $realrm            # 当前shell会被/bin/rm替换掉;exec 将字符串当做命令执行
fi# 解析所有的选项,从中查找-f选项flags=""while getopts "dfiPRrvW" opt;docase $opt inf ) exec $realrm "$@"           ;; # exec调用可以使该脚本直接退出* ) flags="$flags -$opt"        ;; # 将其他选项留给rm命令esac
doneshift $(( $OPTIND - 1 ))# 主脚本开始
# ===================# 确保$archivedir 存在。if [ ! -d $archivedir ];then # 1if [ ! -w $HOME ];thenecho "$0 failed: can't create $archivedir in $HOME" >&2exit 1fimkdir $archivedirchmod 700 $archivedir # 2 请保留点隐私 设置读/写/执行 权限
fifor arg  ;donewname="$archivedir/$(date "+%S.%M.%H.%d.%m").$(basename "arg")" # 3 拼接备份的文件名; basename 从文件目录中截取出文件名if [ -f "$arg" -o -d "$arg" ];then$copy "$arg" "$newname"fidoneexec $realrm $flags "$@" # 4 由 realrm 替换当前shell

运行结果

可以设置别名来安装这个脚本,这样当你输入 rm 的时候,实际上运行的是该脚本,而不是 命令/bin/rm。bash 或 ksh 创建别名的方法如下

alias rm=xxx/xxx/newrm # 使用alias 为命令创建别名$ ls ~/.deleted-files
ls: /Users/taylor/.deleted-files/: No such file or directory
$ newrm file-to-keep-forever
$ ls ~/.deleted-files/
51.36.16.25.03.file-to-keep-forever

脚本16 处理被删除文件的归档

用户的主目录中现在已经出现了一个保存着已删除文件的隐藏目录,如果有个脚本能让用户 在被删除文件的不同版本之间选择,肯定大有帮助。不过,要想搞定所有的情况并非易事,所涉 及的问题包括压根找不到指定的文件、找到了多个符合条件的已删除文件等,不一而足。如果出 现了多个匹配文件,那么脚本是应该自动挑选最近的文件恢复?还是告知用户出现了不止一个匹 配?或是把不同的文件版本全都列出来,让用户自己选?代码清单 2-7 中的 shell 脚本 unrm 详细 给出了应对之道

代码 unrm

#!/bin/bash# unrm -- 在已删除的文件归档中查找指定的文件或目录。如果有多个
# 匹配结果,则按时间戳排序,将结果列出,由用户指定要恢复那个。archivedir="$HOME/.deleted-files"
realrm="$(which rm)"
move="$(which mv)"dest=$(pwd)if [ ! -d $archivedir ];then # -d 判断是否为目录echo "$0: No deleted files directory: nothing to unrm" >&2exit 1
ficd $archivedir# 如果没有提供参数,则只显示已删除文件的列表if [ $# -eq 0 ];then # 1 如果用户没有指定参数,那么执行条件语句块echo "Contents of your deleted files archive (sorted by date):"# 51.36.16.25.03.file-to-keep-forever 中的51.36.16.25.03.删除ls -FC | sed -e 's/\([[:digit:]][[:digit:]]\.\)\{5\}//g' -e 's/^/ /' # 2 -F 只列出目录 -C 按列输出,纵向排序; ls 输出中包括点号在内的时间和日期信息exit 0
fi# 否则,就必须按用户指定的模式来处理
# 让我们看看是否有多个匹配结果# 用户可以把想要恢复的文件或目录名指定为参数。
matches="$(ls -d *"$1" 2> /dev/null |wc -l)" # 3 -d 将目录象文件一样显示,而不是显示其下的文件;为了确保ls能够列出文件名中包含空格的文件,代码中用到了不太常见的嵌套双引号($1两边),而通配符*可以匹配文件名之前的时间戳if [ $matches -eq 0 ];thenecho "No match for \"$1\" in the deleted file archive." >&2exit 1
fiif [ $matches -gt 1 ];then # 4 如果指定的文件或目录名有多个匹配echo "More than one file or directory match in the archive:"index=1for name in $(ls -td *"$1");do # -t 按时间排序datetime="$(echo $name |cut -c1-14|awk -F. '{print $5"/"$4 at "$3":"$2":"$1" }')" # 5 将文件名中的时间戳部分转换成文件被删除的日期和时间filename="$(echo $name |cut -c16-)"if [ -d $name ];thenfilecount="$(ls $name |wc -l |sed 's/[^[:digit:]]//g')" # 6 脚本没有选择显示匹配目录的大小,它显示的是匹配目录中文件的个数,这个数字更为实用echo " $index) $filename (contents = ${filecount} times, deleted = $datetime)"elsesize=" $(ls -sdk1 $name | awk '{print $1}')" # 7  -k 以k 字节的形式表示文件的大小;- 1 以列显示文件echo " $index) $filename (size = ${size}Kb, deleted = $datetime)"fiindex=$((index + 1))doneecho ""read -p "Which version of $1 do you want to restore ('0' to quit)? [1] : " desiredif [ ! -z "$(echo $desired | sed 's/[[:digit:]]//g' ];thenecho "$0: Restore canceled by user: invalid input." >&2exit 1fiif [ ${desired:=-1} -ge $index ];thenecho "$0: Restore canceled by user; index value too big." >&2exit 1fiif [ $desired -lt 1 ];thenecho "$0: restore canceled by user." >&2exit 1firestore="$(ls -td1 *"$1" |sed -n "${desired}p")" # 8 只要客户选定了某个匹配的文件或者目录,就找出相应的文件名; -n 选项时指定行号if [ -e "$dest/$1" ];then # 9 确保unrm不会覆盖文件的现有副本,文件或目录的恢复是通过调用mv来实现echo "\"$1\" already exists in this directory.Cannot overwrite." >&2exit 1fiecho -n "Restoring file \"$1\" ... "$move "$remove" "$dest/$1"echo "done."read -p "Delete the additional copies of this file? [y]" answer # 10 恢复完成后,用户可以选择是否删除该文件的其他副本if [ ${answer:=y} ="y" ];then$realrm -rf *"$1"echo "deleted."elseecho "additional copies retained."fi
elseif [ -e "$dest/$1" ];thenecho "\"$1\" already exists in this directory. Cannot overwrite." >&2exit 1firestore="$(ls -d *"$1")"echo -n "Restoring file \"$1\"..."$move "$restore" "$dest/$1"echo "done"
fiexit 0

运行结果

# 无参数的 shell 脚本 unrm 会列出当前能够恢复的文件
$ unrm
Contents of your deleted files archive (sorted by date):
detritus        this is a test
detritus        garbage# 带参数的 shell 脚本 unrm 会尝试恢复文件
$ unrm
detritus More than one file or directory match in the archive:
1) detritus (size = 7688Kb, deleted = 11/29 at 10:00:12)
2) detritus (size = 4Kb, deleted = 11/29 at 09:59:51)
Which version of detritus should I restore ('0' to quit)? [1] : 0
unrm: Restore canceled by user.

精益求精

在使用这个脚本的时候,一定要注意,如果不加任何控制或限制,已删除文件归档中的文件 和目录就会不停地增长。为了避免出现这种情况,可以在 cron 作业中调用 find 命令来修剪已删 除文件的归档,利用其-mtime 选项筛选出那些长达数周都没用过的文件。一般来说,14 天的归 档差不多已经足以满足大部分用户了,同时也能避免浪费过多的磁盘空间。

尽管功能已经实现了, 不过在用户友好性方面, 脚本还有改进的空间。 考虑添加一些选 项,例如 -l(恢复到文件的最近版本)和 -D(删除文件的副本)。你打算加入哪个选项并如何 处理呢?

脚本17 记录文件删除操作

你也许只是想跟踪记录系统上的删除操作,不打算归档被删除的文件。在代码清单 2-10 中, 使用 rm 命令完成的文件删除操作会在不提醒用户的情况下被记录在一个单独的文件中。这可以 通过编写一个作为包装器的脚本来实现。包装器处在实际的 Unix 命令和用户之间,为用户提供 原始命令所不具备的实用功能。

包装器是一个非常强大的概念,它在本书中会一次又一次地出现。

代码 logrm

#!/bin/bash# logrm -- 记录所有的文件删除操作(除非指定了-s选项).removelog="tmp/remove.log"if [ $# -eq 0 ];then # 1 如果没有提供参数,则生成脚本的用法说明echo "Usage: $0 [-s] list of files or directories" >&2exit 1
fiif [ "$1" = "-s" ];then # 2 检查该参数是否为 -s# 请求静默操作......不记录操作。shift # 左移以为输入的参数
elseecho "$(date):${USER}:$@" >>$removelog # 3 将时间戳、用户名以及命令写入文件$removelog
fi/bin/rm "$@" # 4 用户指定的参数被传给 rmexit 0# 修改rm别名alias rm=logrm

运行结果

$ touch unused.file ciao.c /tmp/junkit
$ logrm unused.file /tmp/junkit
$ logrm ciao.c
$ cat /tmp/remove.log
Thu Apr 6 11:32:05 MDT 2017: susan: /tmp/central.log
Fri Apr 7 14:25:11 MDT 2017: taylor: unused.file /tmp/junkit Fri Apr 7 14:25:14 MDT 2017: taylor: ciao.c

精益求精

这里存在一个潜在的日志文件权限问题。文件 remove.log 要么可以由所有人写入,在这种情 况下,随便哪个用户都可以使用像 cat /dev/null > /tmp/remove.log 这样的命令清空日志文件; 要么所有人都不能写入,如此一来,脚本也就没法记录任何事件了。你可以通过 setuid 权限, 让脚本使用和日志文件相同的权限运行。但这种方法存在两个问题。首先,这着实是个坏主意! 绝对不要用 setuid 运行脚本!利用 setuid 以特定用户的身份运行命令,不管具体的用户是谁, 都有可能给系统引入安全隐患。其次,你可能会碰到用户有权限删除自己的文件,而脚本没权限 删除的情况,因为使用 setuid 设置的有效 uid 会被 rm 命令继承,这可就要出事了。当用户甚至 都无法删除自己的文件时,会造成很大的困惑。

如果你使用了 ext2、ext3 或 ext4 文件系统(在 Linux 中很常见),那么还有别的方法:用 chattr 命令为日志文件设置只能追加(append-only)的文件权限,然后向所有人开放写权限, 这样不会有任何危险。另外还有一种解决方法是通过 logger 命令向 syslog 写入日志消息。使用 logger 记录删除操作非常简单直接,如下所示:

logger -t logrm "${USER:-LOGNAME}: $*"

这会向 syslog 数据流中添加一个条目,该条目普通用户无法处理,其内容包含 logrm、用户 名以及指定的命令。

如果你选择使用 logger ,那么应该查看一下 syslogd(8) ,确保相关配置中没有丢弃优先 级为 user.notice 的日志记录。这基本上都是在文件/etc/syslogd.conf 中指定的

脚本18 显示目录内容

ls 命令有一处地方似乎没什么意义:在列出目录的时候,ls 或是列出其中的文件,或是显 示出其中的子目录本身所占用的磁盘块数(以 1024 字节为单位)。ls -l 的典型输出如下所示:

drwxrwxr-x   2 taylor    taylor  4096 Oct 28 19:07 bin

但这并没有多大用处!我们其实想知道的是目录中有多少文件。这正是代码清单 2-12 中的 脚本所做的。它会生成一个美观的文件和目录的多列列表,显示出文件的大小以及目录中所包含 的文件数量

代码 formatdir

#!/bin/bash# formatdir -- 采用友好且实用的格式输出目录列表
# 注意,一定要确保scriptbc(脚本#9) 处于当前路径中,
# 因为在脚本中会多次调用到它
# 该函数将以kb为单位的文件大小格式化为KB、MB、BG、提高输出的可续性. ./scriptbcreadablesize(){ # 1 # 该函数接收以KB为单位的值,将其转换成最格式的单位输出if [ $1 -ge 1048576 ];thenecho "$(scriptbc -p 2 $1 / 1048576)GB"else [ $1 -ge 1024 ];thenecho "$(scriptbc -p 2 $1 / 1024)MB"elseecho "${1}KB"fi
}#  主脚本
if [ $# -gt 1 ];thenecho "Usage: $0 [dirname]" >&2exit 1
elif [ $# -eq 1 ];then # 指定了其他目录?2 允许用户指定目录,然后使用cd命令将当前shell的工作目录切换到指定位置cd "$@" # 切换到指定目录。if [ $? -ne 0 ];then # $? 返回上一条命令的运行状态exit 1fi
fi
for line in *;doif [ -d "$file" ];thensize=$(ls "$file" |wc -l|sed 's/[^:digit:]]//g') # 3 计算出目录中文件数量(不包过隐藏文件)if [ $size -eq 1 ];thenecho "$file ($size entry)|"elseecho "$file ($size entries)|"fielsesize="$(ls -sk "$file" |awk '{print $1}')"echo "$file ($(readablesize $size))|" # 4 使用 $() 生成的子shell 调用 readablesizefi
# 先使用 sed 将空格穿换成三个脱字符 (^^^)
# 在使用xargs命令合并成对的行,恢复其中的空格
# 最后通过awk 输出对对齐后的两列
done| sed 's/ /^^^/g'|xargs -n 2|sed 's/\^\^\^/ / /g'|awk -F\|'{printf "%-39s %-39\n",$1,$2}' # %-39s 只一个宽度为39的字符 - 表示左对齐 补足以空格填充
exit 0

运行结果

$ formatdir ~
Applications (0 entries)        Classes (4KB)
DEMO (5 entries)                Desktop (8 entries)
Documents (38 entries)          Incomplete (9 entries)
IntermediateHTML (3 entries)    Library (38 entries)
Movies (1 entry)                Music (1 entry)
...

精益求精

有个问题值得考虑:如果你碰上一个用户,喜欢在文件名中用 3 个连续的脱字符,那该怎么 办?这种命名方式相当罕见,我们抽查过一个包含 116 696 个文件的 Linux 系统,所有的文件名 中连一个脱字符都没出现过。但如果真有的话,脚本输出就要乱套了。如果你有所顾忌,可以选 择把空格转换成其他在文件名中更少出现的字符序列。比如 4 个脱字符?或是 5 个?

脚本19 按照文件名定位文件

locate 命令在 Linux 系统中大有用处,但在其他 Unix 流派中并非都能找到这个命令,它通 过搜索预先建立的文件名数据库来匹配用户指定的正则表达式。有没有想过要快速找到主.cshrc 文件(master .cshrc)的位置?来看看 locate 是怎么做的:

$ locate .cshrc
/.Trashes/501/Previous Systems/private/etc/csh.cshrc
/OS9 Snapshot/Staging Archive/:home/taylor/.cshrc
/private/etc/csh.cshrc
/Users/taylor/.cshrc
/Volumes/110GB/WEBSITES/staging.intuitive.com/home/mdella/.cshrc

你可以看到在 OS X 系统中,主.cshrc 文件位于目录/private/etc 下。我们自己编写的 locate 版本在构建内部文件索引时会查看磁盘中所有的文件,无论文件是在垃圾队列(trash queue)中 还是在单独的磁盘卷中,哪怕是隐藏文件也不会放过。这种做法既有优点也有缺点,我们很快就 会讲到。

代码 mklocatedb

#!/bin/bash# mklocatedb -- 使用find命令构建locate的数据库。用户必须以root身份运行该脚本。locatedb="/tmp/locate.db"if [ "$(whoami)" != "root" ];then # 检查当前用户是否为rootecho "Must be root to run this command." >&2exit 1
fifind / -print > $locatedb # print选项使用\n(换行符)分隔输出的每个文件或目录名exit 0

代码 locate

#!/bin/bash# locate -- 在locate的数据库中查找指定的样式locatedb="/tmp/locate.db"exec grep -i "$@" $localtedb

运行结果

$ sudo mklocatedb
Password:
...
多等一会儿。
...
# 我们可以很方便地使用 ls 检查数据库文件大小,如下所示
$ ls -l /tmp/locate.db
-rw-r--r-- 1 root wheel 174088165 Mar 26 10:02 /tmp/locate.db# 现在,万事俱备,可以使用 locate 查找文件了
$ locate -i solitaire
/Users/taylor/Documents/AskDaveTaylor image folders/0-blog-pics/vista-searchsolitaire.png
/Users/taylor/Documents/AskDaveTaylor image folders/8-blog-pics/windows-playsolitaire-1.png
/usr/share/emacs/22.1/lisp/play/solitaire.el.gz
/usr/share/emacs/22.1/lisp/play/solitaire.elc
/Volumes/MobileBackups/Backups.backupdb/Dave's MBP/2014-04-03-163622/BigHD/ Users/taylor/Documents/AskDaveTaylor image folders/0-blog-pics/vista-searchsolitaire.png
/Volumes/MobileBackups/Backups.backupdb/Dave's MBP/2014-04-03-163622/BigHD/
Users/taylor/Documents/AskDaveTaylor image folders/8-blog-pics/windows-playsolitaire-3.png# 你也可以用这个脚本确认有关系统的其他一些有趣的统计信息,例如有多少个 C 源代码 文件:
$ locate '\.c$' | wc -l
1479

精益求精

保持数据库处于合理的更新状态很容易,就像大部分系统的内建命令 locate 那样,安排 cron 在每周夜间凌晨的时候运行 mklocatedb 就行了,也可以根据本地使用模式提高更新频率。和其 他由 root 用户执行的脚本一样,一定要确保脚本自身不能被非 root 用户修改。

这个脚本可改进的一处地方是让 locate 检查其调用状态,如果没有指定样式或文件 locate.db 不存在,输出有意义的错误信息并中止运行。按照目前的写法,脚本输出的是标准的 grep 错误 信息,这没什么太大的用处。更重要的是,让用户可以访问系统中所有文件名(包括那些他们原 本无权查看的)存在着重大的安全问题,这一点我们先前也讨论过。脚本#39 讨论了如何提高该 脚本的安全性。

脚本21 显示不同时区的时间

一个正常的 date 命令最基本的要求是能够显示用户所在时区的日期和时间。但如果用户散 布在多个时区呢?或者更可能的情况是,如果你的朋友和同事分散在各地,你总是搞不清楚卡萨 布兰卡、梵蒂冈或悉尼现在是什么时间该怎么办?

大多数现代 Unix 上的 data 命令都建立在一个令人惊叹的时区数据库之上。该数据库通常位 于目录/usr/share/zoneinfo 中,其中列出了 600 多个区域,详细给出了每个区域相应的 UTC(协调 通用时间,也被称为 GMT 或格林威治标准时间)时区偏移。date 命令关注时区变量 TZ,我们可 以将其设置为数据库中的任何区域:

$ TZ="Africa/Casablanca" date
Fri Apr 7 16:31:01 WEST 2017

但是,大多数系统用户并不习惯指定临时环境变量设置。可以利用 shell 脚本为时区数据库 创建一个更友好的前端。

代码清单 2-19 中的大部分脚本涉及在时区数据库中(通常保存在时区目录下的多个文件中)挖掘并查找匹配指定模式的文件。一旦找到匹配的文件,脚本会抓取时区的全名(就像上例中的 TZ=“Africa/Casablanca”),用其调用 data 命令,设置子 shell 环境。date 命令会检查 TZ 以查看 它所处的时区,至于这是你临时所处的时区,还是长期居住的时区,它并不清楚。

代码 timein

#!/bin/bash# timein -- 显示指定时区或地区的当前时间
# 如果不指定任何参数,则显示UTC/GMT。参数list会显示出已地区的列表
# 注意,它有可能会匹配到区域目录(地域),但只有时区文件(城市)才是有效资格zonedir="/usr/share/zoneinfo"if [ ! -d $zonedir ] ; thenecho "No time zone database at $zonedir." >&2 ; exit 1
fiif [ -d "$zonedir/posix" ] ; thenzonedir=$zonedir/posix        # modern Linux systems
fiif [ $# -eq 0 ] ; thentimezone="UTC"mixedzone="UTC"
elif [ "$1" = "list" ] ; then # 1( echo "All known time zones and regions defined on this system:"cd $zonedirfind -L * -type f -print | xargs -n 2 | \  # -L选项告诉find命令跟随符号链接awk '{ printf "  %-38s %-38s\n", $1, $2 }' ) | moreexit 0
elseregion="$(dirname $1)"zone="$(basename $1)"# 指定的时区是否能直接匹配?如果能直接匹配,则最好。# 否则我们需要继续查找。先来统计匹配次数。matchcnt="$(find -L $zonedir -name $zone -type f -print |wc -l | sed 's/[^[:digit:]]//g' )"# Check if at least one file matchesif [ "$matchcnt" -gt 0 ] ; then# 如果多余一个匹配文件,则退出if [ $matchcnt -gt 1 ] ; thenecho "\"$zone\" matches more than one possible time zone record." >&2echo "Please use 'list' to see all known regions and time zones" >&2exit 1fimatch="$(find -L $zonedir -name $zone -type f -print)"mixedzone="$zone" else # 我们找到的可能是一个匹配的时区地域(time zone region),而不是一个特定的时区(time zone)# 地域和时区的首字母大写,其余的字母小写mixedregion="$(echo ${region%${region#?}} \| tr '[[:lower:]]' '[[:upper:]]')\$(echo ${region#?} | tr '[[:upper:]]' '[[:lower:]]')"mixedzone="$(echo ${zone%${zone#?}} | tr '[[:lower:]]' '[[:upper:]]') \$(echo ${zone#?} | tr '[[:upper:]]' '[[:lower:]]')"if [ "$mixedregion" != "." ] ; then# 只查找特定地域中的特定时区,如果存在多种可能# (例如 Atlantic),让用户指定唯一的匹配match="$(find -L $zonedir/$mixedregion -type f -name $mixedzone -print)"elsematch="$(find -L $zonedir -name $mixedzone -type f -print)"fi# 如果文件完全匹配指定的模式if [ -z "$match"  ] ; then# 检查模式是否太模糊if [ ! -z $(find -L $zonedir -name $mixedzone -type d -print) ] ; thenecho "The region \"$1\" has more than one time zone. " >&2else  # 如果根本没有出现任何匹配。echo "Can't find an exact match for \"$1\". " >&2fiecho "Please use 'list' to see all known regions and time zones." >&2exit 1fifitimezone="$match"
finicetz=$(echo $timezone | sed "s|$zonedir/||g")    # 美化输出echo It\'s $(TZ=$timezone date '+%A, %B %e, %Y, at %l:%M %p') in $nicetzexit 0

运行结果

$ timein
It's Wednesday, April 5, 2017, at 4:00 PM in UTC
$ timein London
It's Wednesday, April 5, 2017, at 5:00 PM in Europe/London
$ timein Brazil
The region "Brazil" has more than one time zone. Please use 'list' to see all known regions and time zones.
$ timein Pacific/Honolulu
It's Wednesday, April 5, 2017, at 6:00 AM in Pacific/Honolulu
$ timein WET
It's Wednesday, April 5, 2017, at 5:00 PM in WET
$ timein mycloset
Can't find an exact match for "mycloset". Please use 'list' to see all known regions and time zones.

精益求精

知道世界各地特定时区的时间是个了不起的本事,对于管理全球网络的系统管理员尤为如 此。但有时候,你其实只是想快速知道两个时区之间的时差而已。可以改进脚本 timein,为用户 提供这种功能。在 timein 的基础上创建一个能够接受两个参数的新脚本 tzdiff。

有了这两个参数,你就可以确定这两个时区的当前时间,然后打印出时差。但要记住,两个 时区之间两小时的时差可以是早两小时(two hours forward), 也可以是晚两小时(two hours backward),这里面的差别非常大。区分早晚时差对于脚本的实用性来说至关重要

shell 脚本实战 三相关推荐

  1. Linux的shell脚本实战之while循环

    Linux的shell脚本实战之while循环 一.使用while循环计算 1.计算从1加到100之和 2.计算1-2+3-4+5....-100的值 二.while语句的三种方式 1.通过输入重定向 ...

  2. Shell脚本实战之文件批量创建和修改

    Shell脚本实战之文件批量创建和修改 一.脚本要求 二.脚本内容 三.脚本运行结果 一.脚本要求 1.所有操作在/python下 2.批量创建12个以py后缀结尾的文件,文件名中必须包含_hcip, ...

  3. Linux下Shell脚本实战之监测磁盘空间

    Linux下Shell脚本实战之监测磁盘空间 一.脚本目的及要求 二.脚本内容 三.运行脚本 一.脚本目的及要求 (1) 监控/home下每各个用户目录的占用磁盘大小 (2) 监控/var/log下前 ...

  4. Linux下的shell脚本实战之用户创建

    Linux下的shell脚本实战之用户创建 一.脚本要求 二.脚本内容 三.运行脚本 一.脚本要求 二.脚本内容 三.运行脚本 一.脚本要求 1.该脚本提示用户创建用户名和密码 2.判断输入的用户名是 ...

  5. Linux下shell脚本实战之批量新建用户

    Linux下shell脚本实战之批量新建用户 一.脚本要求 二.脚本内容 三.运行脚本 一.脚本要求 二.脚本内容 三.运行脚本 一.脚本要求 1.使用提供的user.txt用户列表 2.批量新建us ...

  6. Linux的shell脚本实战之检查主机IP是否存在

    Linux的shell脚本实战之检查主机IP是否存在 一.目的及要求 二.脚本内容 二.运行结果 一.目的及要求 二.脚本内容 二.运行结果 一.目的及要求 检查 192.168.200.130~14 ...

  7. Linux系统shell脚本实战之解决生产ddos攻击

    Linux系统shell脚本实战之解决生产ddos攻击 一.脚本于鏊求 二.脚本内容 三.执行脚本 一.脚本于鏊求 要求屏蔽掉ddos攻击的IP 二.脚本内容 [root@192 scripts]# ...

  8. Liunx下的日志清理shell脚本实战之日志备份

    Liunx下的日志清理shell脚本实战 一.脚本实现功能以及要求 二.shell脚本内容 一.脚本实现功能以及要求 二.shell脚本内容 一.脚本实现功能以及要求 1.日志备份目录,将日志备份到/ ...

  9. linux exec 脚本之家,详解Shell脚本中调用另一个Shell脚本的三种方式

    主要以下有几种方式: Command Explanation fork 新开一个子 Shell 执行,子 Shell 可以从父 Shell 继承环境变量,但是子 Shell 中的环境变量不会带回给父 ...

最新文章

  1. Java开发程序员最值得学习的10大技术
  2. 开发中 常用 js 记录(一)
  3. hihocoder1260,1261 (HASH经典题)
  4. JavaSE--异常信息打印
  5. java 生成印章_印章生成
  6. julia安装源_Ubuntu下安装Julia
  7. 哈工大车万翔教授:NLPer的核心竞争力是什么?
  8. Java快排两种写法
  9. word2019关闭时无响应
  10. Keil5中提示warning C318: can‘t open file ‘xxx.h‘
  11. NLP实践——Few-shot事件抽取《Building an Event Extractor with Only a Few Examples》
  12. 查找linux内核漏洞查用的方法脚本
  13. 颜色的前世今生15·CMYK系统(原色的选择)
  14. Hive之窗口函数(partition) / order by / row_number / date_sub 等函数联合使用案例(9)
  15. 如何设置工业相机中的“自动增益控制”功能?
  16. 代驾小程序源码开发这3大功能不可少
  17. 像逛淘宝一样“办政事”:阿里云在政务领域的实践
  18. mysql 存储 session
  19. 游戏3D建模要美术基础吗?
  20. 闪聚支付 第2章-支付参数配置

热门文章

  1. Node 裁切图片的方法
  2. PureMVC 游戏框架解析
  3. 关于Floyd算法 和 Dijkstra算法
  4. Docker零基础从入门到精通(全)
  5. 关于vc 2008 runtime
  6. 年仅28岁的程序员郭宇,宣布从字节跳动辞职,实现财富自由!
  7. 字符串—解压缩(C语言)
  8. node 安装(新)
  9. 工作11年,35岁,裸辞: 感到前途很迷茫,如何破?
  10. Mac OS系统使用笔记