Links

16.5 文件与归档命令

归档命令

tar

标准UNIX归档实用程序。[1] 最初是一个磁带归档程序,现在已经发展成为一个通用软件包,可以处理所有类型的目标设备的各种归档方式,从磁带驱动器到常规文件,甚至是标准输出(stdout) (参阅样例 3-4)。GNU tar程序已经成为能够接受各种压缩文件的过滤器,例如: **tar czvf archive_name.tar.gz ***,它递归地归档并gzip当前工作目录($PWD)下除点文件之外的目录树中的所有文件。[2]
一些有用的tar选项:
  1. 1.
    -c创建(一个新存档文件)
  2. 2.
    -x解析(现有存档文件)
  3. 3.
    --delete删除(现有存档文件)
    note
    此选项在磁带设备上不起作用。
  4. 4.
    -r追加(文件到现有存档文件)
  5. 5.
    -A追加(tar文件到现有存档文件)
  6. 6.
    -t列出(现有存档文件的内容)
  7. 7.
    -u更新存档文件
  8. 8.
    -d将存档与指定的文件系统进行比较
  9. 9.
    --after-date仅处理指定日期之后带有日期戳的文件
  10. 10.
    -zgzip该存档文件
    (压缩还是解压缩,取决于与-c还是-x选项结合使用)
  11. 11.
    -jbzip2该存档文件
    note
    从损坏的gzip压缩格式的tar存档中恢复数据可能是一件很难的事情。当归档重要文件时,请进行多次备份。

shar

Shell归档实用程序。能够使shell存档文件中的文本和/或二进制文件无需压缩即可连接,生成的存档文件本质上是一个shell脚本,带有 #!/bin/sh标头,包含所有必要的解压命令以及文件本身。目标文件中不可打印对的二进制字符将在输出的shar文件中转化为可打印的ASCII字符。幸好Shar归档命令仍在Usenet新闻组露面,否则shar会被tar/gzip命令取代。相反,unshar命令会解包shar存档文件。
mailshar命令是一个Bash脚本,它使用shar命令将多个文件连接成一个并发送电子邮件。这个脚本支持压缩和uuencoding

ar

用于存档文件的创建和操作的实用程序,主要用于二进制对象文件库。

rpm

红帽包管理器,或是一个提供了源码或二进制存档文件包装器的rpm实用程序。除此之外,它还包括用于安装和检查软件包完整性的命令。
一个简单的 rpm -i package_name.rpm命令通常就能满足安装一个软件包的需求,除此之外还有更多可用的选项。
rpm -qf标识文件来自哪个包。
bash$ rpm -qf /bin/ls
coreutils-5.2.1-31
rpm -qa列举了给定系统上所有已安装的rpm包的完整列表。rpm -qa package_name仅列出与package_name相对应的软件包。
bash$ rpm -qa
redhat-logos-1.1.3-1
glibc-2.2.4-13
cracklib-2.7-12
dosfstools-2.7-1
gdbm-1.8.0-10
ksymoops-2.4.1-1
mktemp-1.5-11
perl-5.6.0-17
reiserfs-utils-3.x.0j-2
...
bash$ rpm -qa docbook-utils
docbook-utils-0.6.9-2
bash$ rpm -qa docbook | grep docbook
docbook-dtd31-sgml-1.0-10
docbook-style-dsssl-1.64-3
docbook-dtd30-sgml-1.0-10
docbook-dtd40-sgml-1.0-11
docbook-utils-pdf-0.6.9-2
docbook-dtd41-sgml-1.0-10
docbook-utils-0.6.9-2

cpio

这种专门的归档复制命令 (复制输入和输出) 已被tar/gzip取代,现在很少见到。但是它仍然有其用途,例如移动目录树。指定适当的块大小 (用于复制) 后,它可能比tar快得多。
样例 16-30. 使用cpio命令移动一个目录树
#!/bin/bash
# 使用cpio命令复制一个目录树
# 使用'cpio'命令的优势:
# 复制速度的优势。它比管道中的'tar'命令快。
# 非常适合复制那些使用'cp'命令
# 可能性能很差的特殊文件 (命名管道等)
ARGS=2
E_BADARGS=65
if [ $# -ne "$ARGS" ]
then
echo "Usage: `basename $0` source destination"
exit $E_BADARGS
fi
source="$1"
destination="$2"
###################################################################
find "$source" -depth | cpio -admvp "$destination"
# ^^^^^ ^^^^^
# 请参阅'find'和'cpio'的info手册来了解这些选项的作用。
# 以上的工作仅与$PWD(当前目录)有关 . . .
# 指定完整路径名。
###################################################################
# 练习:
# --------
# 添加代码以检查'find | cpio'管道的退出状态($?)
# 以及如果出现任何问题,请输出适当的错误消息。
exit $?

rpm2cpio

该命令会从rpm包中解压cpio存档文件。
样例 16-31. 解压一个rpm存档文件
#!/bin/bash
# de-rpm.sh: 解压一个'rpm'存档文件
: ${1?"Usage: `basename $0` target-file"}
# 必须指定一个'rpm'存档文件名作为参数。
TEMPFILE=$$.cpio # 名称“特定的”临时文件。
# $$代表该脚本的进程ID。
rpm2cpio < $1 > $TEMPFILE # 将rpm存档文件
# 转化为cpio存档文件。
cpio --make-directories -F $TEMPFILE -i # 解压cpio存档文件。
rm -f $TEMPFILE # 删除cpio存档文件。
exit 0
# 练习:
# 添加以下功能代码 1) 检查“目标文件”存在并且
# 2) 是一个rpm存档文件。
# 提示: 解析'file'命令的输出。

pax

pax便携式存档交换工具包可促进定期文件备份,并旨在在各种版本的UNIX之间交叉兼容。它旨在取代tarcpio命令。
pax -wf daily_backup.pax ~/linux-server/files
# 创建目标目录中所有文件的tar存档。
# 请注意,pax命令的选项必须顺序正确 --
# pax -fw 会起到完全不同的作用。
pax -f daily_backup.pax
# 列出存档中的文件。
pax -rf daily_backup.pax ~/bsd-server/files
# 将Linux机器上的备份文件恢复到BSD机器上。
请注意,pax可以处理许多标准的归档和压缩命令。

压缩命令

gzip

标准的GNU/UNIX压缩实用程序,取代了劣等的和专有的compress命令。对应的解压缩命令是gunzip,等效于gzip -d
note
-c选项会将gzip的输出传递给标准输出(stdout)。当使用管道传递给其他命令时这很有用。
zcat过滤器将gzip压缩文件解压缩到标准输出(stdout),可能作为管道的输入或重定向。实际上,这是一个用于压缩文件 (包括使用较旧的compress实用工具处理的文件) 的cat命令。zcat命令等同于gzip -dc
note
在某些商业UNIX系统上,zcatuncompress -c的同义词,并且在gzip压缩文件上不起作用。
另请样例 7-7

bzip2

另一个压缩实用程序,通常比gzip效率更高 (但速度较慢),尤其是在大文件上。其对应的解压缩命令是bunzip2
类似于zcat命令,bzcat会解压一个bzipped2文件到标准输出(stdout)
note
较新版本的tar命令已经支持bzip2

compress, uncompress

这是在商业UNIX发行版中发现的较旧的专有压缩实用程序。效率更高的gzip已在很大程度上取代了它。Linux发行版通常包含compress以实现兼容性,尽管gunzip可以解压缩compress处理的存档文件。
znew命令能够将compress压缩文件转换为gzip压缩文件。

sq

这是另一个压缩实用程序,仅对有序的ASCII单词列表起作用。它使用过滤器的标准调用语法,sq < input-file > output-file。速度快,但效率不及gzip。相应的解压缩过滤器是unsq,像sq一样调用即可。
sq的输出可以通过管道传输到gzip以进行进一步压缩。

zip, unzip

与DOS pkzip.exe兼容的跨平台文件归档和压缩实用程序。与 “tarballs” 相比,"zip"存档文件似乎是互联网上更常见的文件交换媒介。

unarc, unarj, unrar

这些Linux实用程序能够解包使用DOS平台上arc.exearj.exe以及rar.exe程序压缩的存档文件。

lzma, unlzma, lzcat

采用高效的Lempel-Ziv-Markov压缩算法。lzma的算法与gzip类似。7-zip网站上有更多信息可供参阅。

xz, unxz, xzcat

一种新的高效压缩工具,向后兼容lzma,并具有类似于gzip的调用语法。有关更多信息,请参见维基百科条目

文件信息命令

file

用于识别文件类型的实用程序。命令file file-name会返回file-name的文件规范,例如ascii文本或数据。它引用自/usr/share/magic/etc/magic/usr/lib/magic中找到的幻数(magic number),取决于当前Linux/UNIX发行版的具体情况。
-f选项会时file命令以批处理模式运行,并从指定文件中读取要分析的文件名列表。当在一个压缩文件上使用-z选项时,会强制尝试分析未压缩文件的类型。
bash$ file test.tar.gz
test.tar.gz: gzip compressed data, deflated,
last modified: Sun Sep 16 13:34:51 2001, os: Unix
bash file -z test.tar.gz
test.tar.gz: GNU tar archive (gzip compressed data, deflated,
last modified: Sun Sep 16 13:34:51 2001, os: Unix)
# 从给定目录下查找sh和Bash脚本:
DIRECTORY=/usr/local/bin
KEYWORD=Bourne
# Bourne和Bourne-Again shell脚本
file $DIRECTORY/* | fgrep $KEYWORD
# 输出:
# /usr/local/bin/burn-cd: Bourne-Again shell script text executable
# /usr/local/bin/burnit: Bourne-Again shell script text executable
# /usr/local/bin/cassette.sh: Bourne shell script text executable
# /usr/local/bin/copy-cd: Bourne-Again shell script text executable
# . . .
样例 16-32. 去除C程序文件注释
#!/bin/bash
# strip-comment.sh: 去除C程序文件注释(/* COMMENT */)
E_NOARGS=0
E_ARGERROR=66
E_WRONG_FILE_TYPE=67
if [ $# -eq "$E_NOARGS" ]
then
echo "Usage: `basename $0` C-program-file" >&2 # 标准错误(stderr)输出错误信息。
exit $E_ARGERROR
fi
# 测试文件类型是否准确。
type=`file $1 | awk '{ print $2, $3, $4, $5 }'`
# "file $1" 输出文件类型 . . .
# awk命令移除第一个字段,文件名 . . .
# 结果赋值给变量"type"。
correct_type="ASCII C program text"
if [ "$type" != "$correct_type" ]
then
echo
echo "This script works on C program files only."
echo
exit $E_WRONG_FILE_TYPE
fi
# 相当神秘的sed脚本:
#--------
sed '
/^\/\*/d
/.*\*\//d
' $1
#--------
# 如果你花几个小时学习sed基础知识,这很容易理解。
# 需要在sed脚本中再添加一行,
# 以处理代码行的同一行后面有注释的情况。
# 这是一项非同寻常的练习。
# 此外,上面的代码删除了带有"*/"的非注释的行 . . .
# 这并不是一个预期的结果。
exit 0
# ----------------------------------------------------------------
# 由于上面的'exit 0',该行以下的代码将无法执行。
# Stephane Chazelas提出以下的替代方案:
usage() {
echo "Usage: `basename $0` C-program-file" >&2
exit 1
}
WEIRD=`echo -n -e '\377'` # 或者 WEIRD=$'\377'
[[ $# -eq 1 ]] || usage
case `file "$1"` in
*"C program text"*) sed -e "s%/\*%${WEIRD}%g;s%\*/%${WEIRD}%g" "$1" \
| tr '\377\n' '\n\377' \
| sed -ne 'p;n' \
| tr -d '\n' | tr '\377' '\n';;
*) usage;;
esac
# 这依然会被这样的代码欺骗:
# printf("/*");
# 或者
# /* /* buggy embedded comment */
#
# 为了应对这种情况(在字符串里的注释,
# 在字符中带有\", \\"的注释 ...),
# 唯一的方法是写一个C解析器 (可能使用lex或yacc)?
exit 0

which

which command给出了command的完整路径。这对于找出系统上是否安装了特定命令或实用程序很有帮助。
$bash which rm
/usr/bin/rm
如果你想了解该命令有趣的使用方法,请参阅样例 36-16

whereis

类似于以上提到的which命令,whereis command给出了command的完整路径,但同时还有它的man手册
$bash whereis rm
rm: /bin/rm /usr/share/man/man1/rm.1.bz2

whatis

whatis command会从whatis数据库中寻找command的相关信息。这对于识别系统命令和重要的配置文件很有用。可以将其视为简化的man命令。
$bash whatis whatis
whatis (1) - search the whatis database for complete words
样例 16-33. 浏览/usr/X11R6/bin
#!/bin/bash
# /usr/X11R6/bin中那些神秘的二进制文件是什么?
DIRECTORY="/usr/X11R6/bin"
# 也可以尝试"/bin"、"/usr/bin"、"/usr/local/bin"等等。
for file in $DIRECTORY/*
do
whatis `basename $file` # 输出有关该二进制文件的信息。
done
exit 0
# 注意:为确保该命令有效,
# 你必须使用/usr/sbin/makewhatis创建一个"whatis"数据库。
# 您可能希望重定向此脚本的输出,就像这样:
# ./what.sh >>whatis.db
# 或者在标准输出(stdout)一页上查看结果,
# ./what.sh | less
另请参阅样例 11-3

vdir

显示详细的目录列表。效果类似于ls -lb
这是其中一个GNU fileutil
bash$ vdir
total 10
-rw-r--r-- 1 bozo bozo 4034 Jul 18 22:04 data1.xrolo
-rw-r--r-- 1 bozo bozo 4602 May 25 13:58 data1.xrolo.bak
-rw-r--r-- 1 bozo bozo 877 Dec 17 2000 employment.xrolo
bash ls -l
total 10
-rw-r--r-- 1 bozo bozo 4034 Jul 18 22:04 data1.xrolo
-rw-r--r-- 1 bozo bozo 4602 May 25 13:58 data1.xrolo.bak
-rw-r--r-- 1 bozo bozo 877 Dec 17 2000 employment.xrolo

locate, slocate

locate命令会前往专门的数据库中搜索文件。slocate命令是locate命令的安全版本 (也可能取别名为slocate)。
$bash locate hickson
/usr/lib/xephem/catalogs/hickson.edb

getfacl, setfacl

这些命令检索设置文件访问控制列表 -- 所有者属组和文件权限。
bash$ getfacl *
# file: test1.txt
# owner: bozo
# group: bozgrp
user::rw-
group::rw-
other::r--
# file: test2.txt
# owner: bozo
# group: bozgrp
user::rw-
group::rw-
other::r--
bash$ setfacl -m u:bozo:rw yearly_budget.csv
bash$ getfacl yearly_budget.csv
# file: yearly_budget.csv
# owner: accountant
# group: budgetgrp
user::rw-
user:bozo:rw-
user:accountant:rw-
group::rw-
mask::rw-
other::r--
查看符号链接指向的文件。
bash$ readlink /usr/bin/awk
../../bin/gawk

strings

使用strings命令可以在二进制文件或数据文件中查找可打印字符串。它将列出在目标文件中找到的可打印字符序列。这便于快速检查核心转储(core dump)或查看未知图像文件 (strings image-file | more可能显示类似JFIF的东西,那么该文件标识为jpeg图形) 。在脚本中,也可以使用grepsed命令解析strings的输出。请参阅样例 11-8样例 11-10
样例 16-34. 一个“改进的”strings命令
#!/bin/bash
# wstrings.sh: "word-strings" (增强的 "strings" 命令)
#
# 此脚本通过对照标准单词列表文件
# 对"strings"的输出进行筛选。
# 这有效地消除了胡言乱语和噪音,
# 并且只输出可识别的单词。
# ===========================================================
# 脚本参数的标准检查
ARGS=1
E_BADARGS=85
E_NOFILE=86
if [ $# -ne $ARGS ]
then
echo "Usage: `basename $0` filename"
exit $E_BADARGS
fi
if [ ! -f "$1" ] # 检查文件是否存在。
then
echo "File \"$1\" does not exist."
exit $E_NOFILE
fi
# ===========================================================
MINSTRLEN=3 # 最小字符串长度。
WORDFILE=/usr/share/dict/linux.words # 字典文件。
# 可以指定每行一个单词格式的单词列表文件。
# 例如,"yawl" 单词列表包,
# http://bash.deta.in/yawl-0.3.2.tar.gz
wlist=`strings "$1" | tr A-Z a-z | tr '[:space:]' Z | \
tr -cs '[:alpha:]' Z | tr -s '\173-\377' Z | tr Z ' '`
# 用'tr'的多次传递来翻译‘strings’命令的输出。
# "tr A-Z a-z" 转换为小写字母。
# "tr '[:space:]'" 将空格转换为'Z'。
# "tr -cs '[:alpha:]' Z" 将非字母字符转化为Z,
# 并且将多个连续的'Z'压缩为一个。
# "tr -s '\173-\377' Z" 将所有在'z'之前的字母转换为'Z'
# 并且将多个连续的'Z'压缩为一个,
# 从而消除那些之前转换中没能成功处理的奇怪字符。
# 最后,"tr Z ' '"将所有的'Z'转换为空白字符,
# 在以下的循环中空白字符将成为单词之间的分隔符。
# ***********************************************************************
# 请注意将'tr'的输出馈送/管道传输回自身的方法,
# 在每次连续传输时具有不同的参数和/或选项。
# ***********************************************************************
for word in $wlist # 重点:
# $wlist不能打引号。
# "$wlist"无效
# 这是为什么?
do
strlen=${#word} # 字符串长度。
if [ "$strlen" -lt "$MINSTRLEN" ] # 跳过短字符串。
then
continue
fi
grep -Fw $word "$WORDFILE" # 仅进行全字匹配。
# ^^^ # "固定字符串" 和
# "全字"选项。
done
exit $?

比较命令

diff, patch

diff: 灵活的文件比较实用程序。它按顺序逐行比较目标文件。在某些应用程序中,例如比较单词词典,在将它们输送到diff之前,建议先通过sortuniq进行过滤。diff file-1 file-2输出文件中不同的行,并带有插入符以显示每个特定的行属于哪个文件。
diff命令的--side-by-side选项在单独的列中逐行输出每个比较的文件,并标出不匹配的行。-c-u选项同样使命令的输出更易于理解。
diff有各种优质的前端程序(frontend),例如sdiffwdiffxdiffmgdiff
如果比较的文件相同,则diff命令返回0的退出状态,如果它们不同则返回1的退出状态(比较二进制文件时返回2)。这允许在shell脚本中的测试结构中使用diff命令(见下文)。
diff命令的常见用途是生成要与patch命令一起使用的差异文件。-e选项输出适合edex脚本的文件。
patch:灵活的版本控制实用程序。给定一个由diff命令生成的差异文件,patch可以将先前版本的软件包(package)升级到新版本。相较分发一整个新修订的软件包,分发一个相对较小的"diff"文件要方便得多。内核"patch"已成为分发Linux内核版本的首选方法。
patch -p1 <patch-file
# 接受'patch-file'中列出的所有更改,
# 并将其应用于引用其的文件。
# 这将使软件包升级到新版本。
给内核打补丁:
cd /usr/src
gzip -cd patchXX.gz | patch -p0
# 使用'patch'更新内核源码。
# 摘自Linux内核文档"README",
# 来自匿名作者(Alan Cox?)。
note
diff命令还可以递归地比较目录 (对于目录中的文件名)。
bash$ diff -r ~/notes1 ~/notes2
Only in /home/bozo/notes1: file02
Only in /home/bozo/notes1: file03
Only in /home/bozo/notes2: file04
使用zdiff命令比较gzip文件。
使用diffstat命令创建来自diff命令输出的直方图(点分布图)。

diff3, merge

diff的扩展版本能够一次比较三个文件。此命令在成功执行后返回0的退出值,但不幸的是,并没有提供有关比较结果的信息。
bash$ diff3 file-1 file-2 file-3
====
1:1c
This is line 1 of "file-1".
2:1c
This is line 1 of "file-2".
3:1c
This is line 1 of "file-3"
merge(三向文件合并)命令是diff3命令的有趣附件。它的语法是merge Mergefile file1 file2。从file1file2的更改将输出到Mergefile。可以将此命令视为patch命令的精简版本。

sdiff

比较和/或编辑两个文件,以便将它们合并到输出文件中。由于其互动性,该命令在脚本中几乎没有用处。

cmp

cmp命令是上述diff命令的一个更简单的版本。diff报告两个文件之间的差异,而cmp仅显示它们的差异点。
note
类似于diff命令,当两个文件相同时cmp返回0的退出状态,当不同时返回1的退出状态。这允许在shell脚本中的测试结构中使用。
样例 16-35. 使用cmp在脚本中比较两个文件
#!/bin/bash
# file-comparison.sh
ARGS=2 # 该脚本期望两个参数。
E_BADARGS=85
E_UNREADABLE=86
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
# "cmp"的输出重定向至/dev/null
# cmp -s $1 $2 有着相同效果 ("-s"是"cmp"命令的静默标志)
# 感谢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
比较gzip文件时请使用zcmp

comm

多功能文件比较实用程序。使用前文件必须是有序的。
comm -options first-file second-file
comm file-1 file-2输出三列:
  • 第 1 列:file-1特有的行
  • 第 2 列:file-2特有的行
  • 第 3 列:共有的行
选项允许隐藏一列或多列的输出。
  • -1 隐藏第1
  • -2 隐藏第2
  • -3 隐藏第3
  • -12 隐藏第1和第2列,以此类推
在比较“字典”和单词列表(有序的一行一个单词的文本文件)时该命令很实用。

实用工具

basename

从文件名中剥离路径信息,仅打印文件名。basename $0能够让脚本知道它的名字,也就是它被调用的名字。这可以用于提示脚本的"使用方法",例如,如果调用脚本时缺少参数:
echo "Usage: `basename $0` arg1 arg2 ... argn"

dirname

从文件名中剥离basename,仅打印路径信息。
note
basenamedirname可以对任意字符串进行操作。该参数不需要引用现有文件,甚至不需要引用该文件的文件名。请参阅样例 A-7
样例 16-36. basenamedirname
#!/bin/bash
address=/home/bozo/daily-journal.txt
echo "Basename of /home/bozo/daily-journal.txt = `basename $address`"
echo "Dirname of /home/bozo/daily-journal.txt = `dirname $address`"
echo
echo "My own home is `basename ~/`." # `basename ~` 同样有效。
echo "The home of my home is `dirname ~/`." # `dirname ~` 同样有效。
exit 0

split, csplit

这些是用于将文件拆分为较小的块的实用程序。它们通常用于拆分大文件,以便将它们备份在软盘上或者准备通过电子邮件发送或上传。
csplit命令根据上下文拆分文件,当模式匹配成功时则进行拆分。
样例 16-37. 将自己分区域复制的脚本
#!/bin/bash
# splitcopy.sh
# 这是一个将自己拆分为块,
# 然后将这些块重新组合为原脚本拷贝件的脚本。
CHUNKSIZE=4 # 第一个所拆分文件块的大小。
OUTPREFIX=xx # 默认的"csplit"的前缀为"xx"
csplit "$0" "$CHUNKSIZE"
# 一些用于填充的注释行 . . .
# Line 15
# Line 16
# Line 17
# Line 18
# Line 19
# Line 20
cat "$OUTPREFIX"* > "$0.copy" # 连接这些块。
rm "$OUTPREFIX"* # 删除这些块。
exit $?

加密与解密命令

sum, cksum, md5sum, sha1sum

这些是生成校验和的实用程序。校验和是根据文本内容进行数学计算后的数字[3],目的是检查其完整性。出于安全目的,脚本可能会引用校验和列表,例如确保关键系统的文件内容没有被更改或损坏。对于安全应用程序,请使用md5sum (消息摘要 5 校验和) 命令,或者更优的sha1sum (安全哈希算法)。[4]
bash$ cksum /boot/vmlinuz
1670054224 804083 /boot/vmlinuz
bash$ echo -n "Top Secret" | cksum
3391003827 10
bash$ md5sum /boot/vmlinuz
0f43eccea8f09e0a0b2b5cf1dcf333ba /boot/vmlinuz
bash$ echo -n "Top Secret" | md5sum
8babc97a6f62a4649716f4df8d61728f -
note
cksum命令会以byte的形式显示目标文件或标准输出(stdout)的大小。
md5sumsha1sum命令从标准输出(stdout)中接受输入时会显示一个短划线
样例 16-38. 检查文件完整性
#!/bin/bash
# file-integrity.sh: 检查给定目录中的文件是否被篡改。
E_DIR_NOMATCH=80
E_BAD_DBFILE=81
dbfile=File_record.md5
# 用于存储记录的文件名 (数据库文件)。
set_up_database ()
{
echo ""$directory"" > "$dbfile"
# 在文件第一行写入目录名。
md5sum "$directory"/* >> "$dbfile"
# 追加md5校验和和文件名。
}
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" ]
# 跳过校验和数据库文件,
# 因为它会随着脚本的每次调用而改变。
# ---
# 不幸的是,这意味着在$PWD上运行此脚本时,
# 不会检测到校验和数据库文件的篡改。
# 练习:请修复这个问题。
then
echo "${filename[n]} : CHECKSUM ERROR!"
# 自从上次检查前文件已经被更改。
fi
fi
let "n+=1"
done <"$dbfile" # 从校验和数据库中读取。
}
# =================================================== #
# 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
# 为了更彻底的文件完整性检查,
# 你可以考虑使用"Tripwire"包,
# http://sourceforge.net/projects/tripwire/。
了解更多md5sum命令的有趣用法,另请参阅样例 A-19样例 36-16样例 10-2
note
有报道称,128位md5sum可以被破解,因此更安全的160位sha1sum是校验和工具包的一个受欢迎的新成员。
bash$ md5sum testfile
e181e2c8720c60522c4c4c981108e367 testfile
bash$ sha1sum testfile
5d7425a9c08a66c3177f1e31286fa40986ffc996 testfile
安全顾问已经证明即使是sha1sum也可能被破解。幸运的是,较新的Linux发行版包括更长加密位的sha224sumsha256sumsha384sumsha512sum命令。

uuencode

该实用程序将二进制文件 (图像,声音文件,压缩文件等) 编码为ASCII字符,使其适合在电子邮件正文或新闻组发布中传输。当在MIME (多媒体) 编码不可用的情况下尤其有用。

uudecode

它反转了编码过程,解码uuencode编码文件成为原先的二进制文件。
样例 16-39. 使用uudecode解码uuencode编码文件
#!/bin/bash
# 在当前的工作目录下使用uudecode解码所有使用uuencode编码的文件。
lines=35 # 允许35行的头部空间(非常慷慨)。
for File in * # 测试在$PWD下的所有文件。
do
search1=`head -n $lines $File | grep begin | wc -w`
search2=`tail -n $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
fold -s命令可能便于处理(可能在管道中)从Usenet新闻组下载的长uudecode文本消息。