高级Bash脚本编程指南(29):文件与归档命令(二)
成于坚持,败于止步
Comparison
diff, patch
diff: 一个非常灵活的文件比较工具. 这个工具将会以一行接一行的形式来比较目标文件. 在某些应用中, 比如说比较单词词典, 在通过管道将结果传递给diff命令之前, 使用诸如sort和uniq命令来对文件进行过滤将是非常有用的.
diff file-1 file-2 将会输出两个文件中不同的行, 并会通过符号标识出每个不同行所属的文件.
diff命令的--side-by-side选项将会按照左右分隔的形式, 把两个比较中的文件全部输出, 并且会把不同的行标记出来. -c和-u选项也会使得diff命令的输出变得容易解释一些.
还有一些diff命令的变种, 比如sdiff, wdiff, xdiff, 和mgdiff.
如果比较的两个文件是完全一样的话, 那么diff命令会返回0作为退出状态码, 如果不同的话就返回1作为退出码. 这样diff命令就可以用在shell脚本的测试结构中了.
diff命令的一个重要用法就是产生区别文件, 这个文件将用作patch命令的-e选项的参数, -e选项接受ed或ex脚本.
patch: 灵活的版本工具. 给出一个用diff命令产生的区别文件, patch命令可以将一个老版本的包更新为一个新版本的包. 因为你发布一个小的"区别"文件远比重新发布一个大的软件包来的容易得多. 对于频繁更新的Linux内核来说, 使用内核"补丁包"的形式来发布是一种非常好的办法.
patch -p1 <patch-file
上面命令在'patch-file'中取得所有的修改列表, 然后把它们更新到相应的文件中. 那么这个包就被更新为新版本了.
更新内核的方法:
cd /usr/src
gzip -cd patchXX.gz | patch -p0
上面方法使用'patch'来更新内核源文件.
diff也可以递归的比较目录下的所有文件(包含子目录).
使用zdiff来比较gzip文件.
diff3
这是diff命令的扩展版本, 可以同时比较三个文件. 如果成功执行那么这个命令就返回0, 但不幸的是这个命令不给出比较结果的信息.
sdiff
比较和(或)编辑两个文件, 将它们合并到一个输出文件中. 由于这个命令的交互特性, 所以在脚本中很少使用这个命令.
cmp
cmp命令是上边diff命令的一个简单版本. diff命令会报告两个文件的不同之处, 而cmp命令仅仅指出哪些位置有所不同, 不会显示不同之处的具体细节.
就像diff命令那样, 如果两个文件相同的话, cmp将返回0作为退出状态码, 如果不同就返回1. 这样能用在shell脚本的测试结构中了.
看一个实例:在一个脚本中使用cmp命令来比较两个文件
#!/bin/bash
ARGS=2 # 脚本需要两个参数.
E_BADARGS=65
E_UNREADABLE=66
if [ $# -ne "$ARGS" ]
then
echo "Usage: `basename $0` file1 file2"
exit $E_BADARGS
fi
if [[ ! -r "$1" || ! -r "$2" ]]
then
echo "Both files to be compared must exist and be readable."
exit $E_UNREADABLE
fi
cmp $1 $2 &> /dev/null # /dev/null将会禁止"cmp"命令的输出.
# cmp -s $1 $2 与上边这句的结果相同("-s"选项是禁止输出(silent)标志)
# 感谢Anders Gustavsson指出这点.
#
# 使用'diff'命令也可以, 比如, diff $1 $2 &> /dev/null
if [ $? -eq 0 ] # 测试"cmp"命令的退出状态.
then
echo "File \"$1\" is identical to file \"$2\"."
else
echo "File \"$1\" differs from file \"$2\"."
fi
exit 0
结果:
root@ubuntu:~/resource/shell-study/0622-2013# chmod +x test5.sh
root@ubuntu:~/resource/shell-study/0622-2013# ./test5.sh test1.sh test2.sh
File "test1.sh" differs from file "test2.sh".
root@ubuntu:~/resource/shell-study/0622-2013#
使用zcmp处理gzip文件.
comm
多功能的文件比较工具. 使用这个命令之前必须先排序.
comm -options first-file second-file
comm file-1 file-2 将会输出3列:
第1列 = 只在file-1中存在的行
第2列 = 只在file-2中存在的行
第3列 = 两边相同的行.
下列选项可以禁止一列或多列的输出.
-1 禁止显示第1列
-2 禁止显示第2列
-3 禁止显示第3列
-12 禁止第1列和第2列, 等等. (就是说选项可以组合)
root@ubuntu:~/resource/shell-study/0622-2013# cat file1
123456
hello
654321
boy
root@ubuntu:~/resource/shell-study/0622-2013# cat file2
123456
hi
654321
girl
cat
root@ubuntu:~/resource/shell-study/0622-2013# comm file1 file2
123456
hello
comm: file 1 is not in sorted order
654321
boy
hi
comm: file 2 is not in sorted order
654321
girl
cat
root@ubuntu:~/resource/shell-study/0622-2013# comm -1 file1 file2
123456
comm: file 1 is not in sorted order
hi
comm: file 2 is not in sorted order
654321
girl
cat
root@ubuntu:~/resource/shell-study/0622-2013# comm -2 file1 file2
123456
hello
comm: file 1 is not in sorted order
654321
boy
comm: file 2 is not in sorted order
root@ubuntu:~/resource/shell-study/0622-2013# comm -12 file1 file2
123456
comm: file 1 is not in sorted order
comm: file 2 is not in sorted order
root@ubuntu:~/resource/shell-study/0622-2013#
Utilities
basename
从文件名中去掉路径信息, 只打印出文件名. 结构basename $0可以让脚本获得它自己的名字, 也就是, 它被调用的名字. 可以用来显示"用法"信息, 比如如果你调用脚本的时候缺少参数, 可以使用如下语句:
echo "Usage: `basename $0` arg1 arg2 ... argn"
dirname
从带路径的文件名字符串中去掉文件名(basename), 只打印出路径信息.
basename和dirname可以操作任意字符串. 它们的参数不一定是一个真正存在的文件, 甚至可以只是一个文件名
#!/bin/bash
a=/home/bozo/daily-journal.txt
echo "Basename of /home/bozo/daily-journal.txt = `basename $a`"
echo "Dirname of /home/bozo/daily-journal.txt = `dirname $a`"
echo
echo "My own home is `basename ~/`." # `basename ~` 也可以.
echo "The home of my home is `dirname ~/`." # `dirname ~` 也可以.
exit 0
结果:
root@ubuntu:~/resource/shell-study/0622-2013# chmod +x test6.sh
root@ubuntu:~/resource/shell-study/0622-2013# ./test6.sh
Basename of /home/bozo/daily-journal.txt = daily-journal.txt
Dirname of /home/bozo/daily-journal.txt = /home/bozo
My own home is root.
The home of my home is /.
root@ubuntu:~/resource/shell-study/0622-2013#
split, csplit
将一个文件分割为几个小段的工具. 这些命令通常会将大的文件分割, 然后备份到软盘上, 或者是为了将大文件切成合适的尺寸, 然后用email上传.csplit命令会根据上下文来切割文件, 切割的位置将会发生在模式匹配的地方.
sum, cksum, md5sum, sha1sum
这些都是用来产生checksum的工具. checksum是对文件的内容进行数学计算而得到的, 它的目的是用来检验文件的完整性, 出于安全目的一个脚本可能会有一个checksum列表, 这样可以确保关键系统文件的内容不会被修改或损坏. 对于需要安全性的应用来说, 应该使用md5sum (message digest 5 checksum)命令, 或者使用更好更新的sha1sum命令(安全Hash算法).
root@ubuntu:~/resource/shell-study/0622-2013# cksum /boot/
abi-2.6.32-21-generic initrd.img-2.6.32-21-generic vmcoreinfo-2.6.32-21-generic
config-2.6.32-21-generic memtest86+.bin vmlinuz-2.6.32-21-generic
grub/ System.map-2.6.32-21-generic
root@ubuntu:~/resource/shell-study/0622-2013# cksum /boot/vm
vmcoreinfo-2.6.32-21-generic vmlinuz-2.6.32-21-generic
root@ubuntu:~/resource/shell-study/0622-2013# cksum /boot/vmlinuz-2.6.32-21-generic
3690579176 4029792 /boot/vmlinuz-2.6.32-21-generic
root@ubuntu:~/resource/shell-study/0622-2013# echo -n "Top Sercret" | cksum
2021153305 11
root@ubuntu:~/resource/shell-study/0622-2013# md5sum /boot/vmlinuz-2.6.32-21-generic
1c0d4864792ec0bb7660f303f805a2fe /boot/vmlinuz-2.6.32-21-generic
root@ubuntu:~/resource/shell-study/0622-2013# echo -n "Top Sercret" | md5sum
cc20b742f8db0c240be6ad0b846d8cb8 -
root@ubuntu:~/resource/shell-study/0622-2013#
一个实例:检查文件完整性
#!/bin/bash
# 检查一个给定目录下的文件是否被改动了.
E_DIR_NOMATCH=70
E_BAD_DBFILE=71
dbfile=File_record.md5
# 存储记录的文件名(数据库文件).
set_up_database ()
{
echo ""$directory"" > "$dbfile"
# 把目录名写到文件的第一行.
md5sum "$directory"/* >> "$dbfile"
# 在文件中附上md5 checksum和filename.
}
check_database ()
{
local n=0
local filename
local checksum
# ------------------------------------------- #
# 这个文件检查其实是不必要的,
#+ 但是能更安全一些.
if [ ! -r "$dbfile" ]
then
echo "Unable to read checksum database file!"
exit $E_BAD_DBFILE
fi
# ------------------------------------------- #
while read record[n]
do
directory_checked="${record[0]}"
if [ "$directory_checked" != "$directory" ]
then
echo "Directories do not match up!"
# 换个目录试一下.
exit $E_DIR_NOMATCH
fi
if [ "$n" -gt 0 ] # 不是目录名.
then
filename[n]=$( echo ${record[$n]} | awk '{ print $2 }' )
# md5sum向后写记录,
#+ 先写checksum, 然后写filename.
checksum[n]=$( md5sum "${filename[n]}" )
if [ "${record[n]}" = "${checksum[n]}" ]
then
echo "${filename[n]} unchanged."
elif [ "`basename ${filename[n]}`" != "$dbfile" ]
# 跳过checksum数据库文件,
#+ 因为在每次调用脚本它都会被修改.
# ---
# 这不幸的意味着当我们在$PWD中运行这个脚本时侯,
#+ 篡改这个checksum数
#+ 据库文件将不会被检测出来.
# 练习: 修正这个问题.
then
echo "${filename[n]} : CHECKSUM ERROR!"
# 从上次的检查之后, 文件已经被修改.
fi
fi
let "n+=1"
done <"$dbfile" # 从checksum数据库文件中读.
}
# =================================================== #
# main ()
if [ -z "$1" ]
then
directory="$PWD" # 如果没指定参数的话,
else #+ 那么就使用当前的工作目录.
directory="$1"
fi
#clear # 清屏.
echo " Running file integrity check on $directory"
echo
# ------------------------------------------------------------------ #
if [ ! -r "$dbfile" ] # 是否需要建立数据库文件?
then
echo "Setting up database file, \""$directory"/"$dbfile"\"."; echo
set_up_database
fi
# ------------------------------------------------------------------ #
check_database # 调用主要处理函数.
echo
# 你可能想把这个脚本的输出重定向到文件中,
#+ 尤其在这个目录中有很多文件的时候.
exit 0
结果:
root@ubuntu:~/resource/shell-study/0622-2013# ls
file1 file2 test1.sh test2.sh test3.sh test4.sh test5.sh test6.sh test7.sh
root@ubuntu:~/resource/shell-study/0622-2013# ./test7.sh
Running file integrity check on /root/resource/shell-study/0622-2013
Setting up database file, "/root/resource/shell-study/0622-2013/File_record.md5".
/root/resource/shell-study/0622-2013/file1 unchanged.
/root/resource/shell-study/0622-2013/file2 unchanged.
/root/resource/shell-study/0622-2013/test1.sh unchanged.
/root/resource/shell-study/0622-2013/test2.sh unchanged.
/root/resource/shell-study/0622-2013/test3.sh unchanged.
/root/resource/shell-study/0622-2013/test4.sh unchanged.
/root/resource/shell-study/0622-2013/test5.sh unchanged.
/root/resource/shell-study/0622-2013/test6.sh unchanged.
/root/resource/shell-study/0622-2013/test7.sh unchanged.
root@ubuntu:~/resource/shell-study/0622-2013# vi test1.sh
root@ubuntu:~/resource/shell-study/0622-2013# ./test7.sh
Running file integrity check on /root/resource/shell-study/0622-2013
/root/resource/shell-study/0622-2013/file1 unchanged.
/root/resource/shell-study/0622-2013/file2 unchanged.
/root/resource/shell-study/0622-2013/test1.sh : CHECKSUM ERROR!
/root/resource/shell-study/0622-2013/test2.sh unchanged.
/root/resource/shell-study/0622-2013/test3.sh unchanged.
/root/resource/shell-study/0622-2013/test4.sh unchanged.
/root/resource/shell-study/0622-2013/test5.sh unchanged.
/root/resource/shell-study/0622-2013/test6.sh unchanged.
/root/resource/shell-study/0622-2013/test7.sh unchanged.
root@ubuntu:~/resource/shell-study/0622-2013# ls
file1 file2 File_record.md5 test1.sh test2.sh test3.sh test4.sh test5.sh test6.sh test7.sh
root@ubuntu:~/resource/shell-study/0622-2013#
root@ubuntu:~/resource/shell-study/0622-2013# cat File_record.md5
/root/resource/shell-study/0622-2013
706ff1928dec96f2bf6e4ddf61db5856 /root/resource/shell-study/0622-2013/file1
c4396191d66ed8992ad4bec7579eb87b /root/resource/shell-study/0622-2013/file2
1204c3876839d879b6fa70c0c6b22394 /root/resource/shell-study/0622-2013/File_record.md5
2ea9a2e6384be12ac0dcdadbce144a3a /root/resource/shell-study/0622-2013/test1.sh
b1dd05cfb334ee0e8a56fbcbd4e61f09 /root/resource/shell-study/0622-2013/test2.sh
329c96ec93b6ac40c2f5cdc355ae064f /root/resource/shell-study/0622-2013/test3.sh
a474a06fcf0983153f256db619055643 /root/resource/shell-study/0622-2013/test4.sh
0c26226596e42d1cc5f6db7b0c08667a /root/resource/shell-study/0622-2013/test5.sh
609b3360144ed918dd093b7b5b17184c /root/resource/shell-study/0622-2013/test6.sh
ff08054c16d2e70cce93437e039c5f6a /root/resource/shell-study/0622-2013/test7.sh
root@ubuntu:~/resource/shell-study/0622-2013#
shred
用随机字符填充文件, 使得文件无法恢复, 这样就可以保证文件安全的被删除, 但是使用这个命令是一种更优雅更彻底的方法.其实即使使用了shred命令, 高级的辨别技术也还是能够恢复文件的内容.O(∩_∩)O~
root@ubuntu:~/resource/shell-study/0622-2013# cat file1
123456
hello
654321
boy
root@ubuntu:~/resource/shell-study/0622-2013# shred file1
root@ubuntu:~/resource/shell-study/0622-2013# cat file1
��W��|�Y'���4�����D�v
���Up��JE��]E���m'mqM{D�9��;�D5�+�A�3�&f"�l|�@�`���P`_�X��t�i�j�t��,�K��a�p�����@�b��}��'��e[��1���ݯP�5M����%_#��9�)����9(�:8Yo>��J!�$.��Z�\��
root@ubuntu:~/resource/shell-study/0622-2013#
看到产生的一堆随机字符,这样之后然后再删除文件达到上面说的目的
编码和解码
uuencode
这个工具用来把二进制文件编码成ASCII字符串, 这个工具适用于编码e-mail消息体, 或者新闻组消息.
uudecode
这个工具用来把uuencode后的ASCII字符串恢复为二进制文件.
一个实例:Uudecode编码后的文件
#!/bin/bash
# 在当前目录下用Uudecode解码所有用uuencode编码的文件.
lines=35 # 允许读头部的35行(范围很宽).
for File in * # 测试所有$PWD下的文件.
do
search1=`head -$lines $File | grep begin | wc -w`
search2=`tail -$lines $File | grep end | wc -w`
# 用Uuencode编码过的文件在文件开始的地方都有个"begin",
#+ 在文件结尾的地方都有"end".
if [ "$search1" -gt 0 ]
then
if [ "$search2" -gt 0 ]
then
echo "uudecoding - $File -"
uudecode $File
fi
fi
done
# 小心不要让这个脚本运行自己,
#+ 因为它也会把自身也认为是一个经过uuencode编码过的文件,
#+ 这都是因为这个脚本自身也包含"begin"和"end".
exit 0
mimencode, mmencode
mimencode和mmencode命令用来处理多媒体编码的email附件. 虽然mail用户代理(比如pine或kmail)通常情况下都会自动处理, 但是这些特定的工具允许从命令行或shell脚本中来手动操作这些附件.
crypt
这个工具曾经是标准的UNIX文件加密工具. 政府由于政策上的动机规定禁止加密软件的输出, 这样导致了crypt命令从UNIX世界消失, 并且在大多数的Linux发行版中也没有这个命令. 幸运的是, 程序员们想出了一些替代它的方法, 在这些方法中有作者自己的cruft
Miscellaneous
mktemp
使用一个"唯一"的文件名来创建一个临时文件.如果不带参数的在命令行下调用这个命令时, 将会在/tmp目录下产生一个零长度的文件.
root@ubuntu:~/resource/shell-study/0622-2013# mktemp
/tmp/tmp.N4A1vdapbN
root@ubuntu:~/resource/shell-study/0622-2013# ls -l /tmp/tmp.N4A1vdapbN
-rw------- 1 root root 0 2013-06-21 19:42 /tmp/tmp.N4A1vdapbN
root@ubuntu:~/resource/shell-study/0622-2013#
root@ubuntu:~/resource/shell-study/0622-2013# mktemp file.XXXXXXXX
file.EfieeYhy
root@ubuntu:~/resource/shell-study/0622-2013#
在这个临时的文件名中 至少需要8个占位符. 如果没有指定临时文件的文件名, 那么默认就是"tmp.XXXXXXXXXX",上面生成的tmp.N4A1vdapbN也验证了这一点
make
build(建立)和compile(编译)二进制包的工具. 当源文件被增加或修改时就会触发一些操作, 这个工具用来控制这些操作.
make命令会检查Makefile, makefile是文件的依赖和操作列表.
install
特殊目的的文件拷贝命令, 与cp命令相似, 但是具有设置拷贝文件的权限和属性的能力. 这个命令看起来是为了安装软件包所定制的, 而且就其本身而言, 这个命令经常出现在Makefiles中(在make install : 区域中). 在安装脚本中也会看到这个命令的使用.
dos2unix
这个工具是由Benjamin Lin及其同事共同编写的, 目的是将DOS格式的文本文件(以CR-LF为行结束符)转换为UNIX格式(以LF为行结束符), 反过来也一样.
ptx
ptx [targetfile]命令将输出目标文件的序列改变索引(交叉引用列表). 如果必要的话, 这个命令可以在管道中进行更深层次的过滤和格式化.
more, less
分页显示文本文件或stdout, 一次一屏. 可以用来过滤stdout的输出 . . . 或过滤一个脚本的输出.
more命令的一个有趣的应用就是测试一个命令序列的执行, 这样做的目的是避免可能发生的糟糕的结果.
root@ubuntu:~/resource/shell-study/0622-2013# ls -l /usr/bin/
......
-rwxr-xr-x 1 root root 76060 2010-02-15 09:40 zipnote
-rwxr-xr-x 1 root root 80156 2010-02-15 09:40 zipsplit
-rwxr-xr-x 1 root root 57588 2010-03-25 01:12 zjsdecode
-rwxr-xr-x 1 root root 88168 2010-03-02 02:31 zsoelim
......
root@ubuntu:~/resource/shell-study/0622-2013# ls -l /usr/bin/ | awk '{print $6}' | more
2010-03-04
2012-11-30
2010-04-16
2010-04-23
2012-11-30
2010-03-28
2010-04-23
2010-01-29
2010-04-14
2010-03-22
2010-04-18
2010-03-29
2010-03-28
2010-03-28
2010-03-28
2009-11-13
2009-11-26
2010-03-28
2010-03-28
--More--
点击空格键翻页,q退出
先到这里了,O(∩_∩)O~
我的专栏地址:http://blog.csdn.net/column/details/shell-daily-study.html
待续。。。。