高级Bash脚本编程指南(29):文件与归档命令(二)

高级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

待续。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值