我有一堆文件,需要验证它们的校验和。我有一个文本文件,如下所示:
校验<tab>
和文件名<new line>
我想我可以用它作为练习来改进我的 shell 脚本。这就是我想出的方法,它成功了,我只是好奇是否有更好的方法。我意识到它不是很灵活(例如假设文件的格式和算法是256)。但我试图避免cat
...... echo
:)
谢谢!
#!/bin/sh
workingDir="/path/to/directory/"
textFile="checksums.txt"
filePath="$workingDir$textFile"
while read a b; do
shasumOutput=$(/usr/bin/shasum -a 256 "$workingDir$b" | /usr/bin/awk '{ print $1 }')
if [ "$a" = "$shasumOutput" ]; then
/usr/bin/printf "$b checksum matches: "$a", "$shasumOutput"\n"
else
/usr/bin/printf "$b checksum doesn't match: "$a", "$shasumOutput"\n"
fi
done < "$filePath"
答案1
作为Gohu指出shasum
在评论中,您正在复制已经对其标志进行的检查-c
。
的输入shasum -c
应该是 产生的内容shasum
。
忽略这一点...
你的脚本看起来不错,但我可以评论其中的一些内容。
这些printf
行最好写成printf 'format string' "$var1" "$var2" "etc."
, 例如 而不是
/usr/bin/printf "$b checksum matches: "$a", "$shasumOutput"\n"
使用
printf '%s checksum matches: %s, %s\n' "$b" "$a" $shasumOutput"
使用的全部要点printf
是,您有一个静态格式字符串,后跟几位进入格式字符串模板的变量数据。
另外,为了避免$( shasum ... )
和减少对外部实用程序的调用次数,我会这样做:
#!/bin/sh
checkdir='/some/path'
checkfile="$checkdir/checksums.txt"
while read -r checksum filename; do
if [ ! -f "$checkdir/$filename" ]; then
printf 'Not found: %s\n' "$filename"
continue
fi
gsha256sum "$checkdir/$filename" | {
read -r realsum name
if [ "$realsum" != "$checksum" ]; then
printf 'Mismatch for "%s":\n\t%s != %s\n' \
"$filename" "$checksum" "$realsum"
else
printf '%s OK\n' "$filename"
fi
}
done <"$checkfile" >&2
这将外部实用程序调用的数量(在大多数 shell 中)减少到一次(SHA256 实用程序)。
在我的 OpenBSD 系统上生成 SHA256 校验和的 GNU coreutils 实用程序称为gsha256sum
。我假设它的输出与shasum -a 256
您的系统上的输出相同。
一些注意事项:
我通常不会放在
/
目录名称的末尾。相反,我在使用变量时插入分隔符。这样我可以直接看到这$checkdir/checksum.txt
是一个文件的路径,而${checkdir}checksum.txt
(或类似的)则更加模糊。在尝试验证其校验和之前,我检查该文件是否确实存在。
我没有使用 解析 SHA256 程序的输出
awk
,而是以与从校验和文件中读取校验和和文件名相同的方式读取它。我在一个上下文中这样做{ ...; }
。我正在治疗全部循环的输出作为“诊断消息”,并使用
>&2
after将其全部重定向到标准错误done
。我倾向于不在脚本中使用实用程序的绝对路径,除非有真正的理由这样做。
printf
例如,几乎总是 shell 内置实用程序,很少有理由/usr/bin/printf
显式使用。
您很可能通过使用输入文件中的所有文件名来调用 SHA256 实用程序,然后比较该调用的校验和,从而对 SHA256 实用程序进行一次调用(或很少的调用),但代码可能会稍微复杂一些,而且工作量会很大。如果这是一次性的事情并且输入文件不是很大,那么就不值得。