Lane East 的 blog

一百年很短,一秒钟很长

想写个比较兼容的脚本也不容易啊

2008-06-19 06:17

最近在写个脚本玩, 突然觉得, 也许该写个尽可能能用在不同的 *nix 的系统上的脚本(不敢说 posix 兼容), 为了实验, 找了一台 SunOS 5.10 的机器, 还有一台 FreeBSD 6.2-RELEASE 的机器来实验(之前注册的 unix center 的账号派上用场了, 在此表示感谢:D )

写脚本的时候, 想要打 .tar.gz 的包, 考虑到有不支持 z 参数的 tar 命令( SunOS 的就不支持, 不过可以用 gtar 来执行 GNU tar ), 所以使用 tar 打包后用管道交给 gzip 压缩. 先是在自己的 vmware 上装的 slackware 9.1 里面写(远程写代码的时候有时候感觉会有一点点卡, 而身边正好有很多年前找哥们帮刻的 slackware 9.1 的盘, 就用虚拟机装上了), 很简单: tar c . | gzip -9 > ../test.tar.gz

运行着也没问题, 本来觉得, 这么简单的一句, 应该是没什么问题的, 结果拿到 SunOS 上一执行, 提示: tar: /dev/rmt/0: Permission denied

看来在不指定打包的文件的时候, SunOS 的 tar 直接把打包的内容往磁带上面放了, 不过这也很正常, tar 本来就是 tape archive 的意思嘛, 所以, 这一句简单的命令就改成了: tar cf - . | gzip -9 > ../test.tar.gz

用这一句在 Slackware, SunOS, FreeBSD 上试了一下, 都没问题了 :D

PS: tar c . | gzip -9 > ../test.tar.gz 在 FreeBSD 上也是不行的, 提示是: tar: Failed to open '/dev/sa0': Operation not supported

然后突然想到, 当前目录下面的某一个目录不需要打包, 这个, 如果只考虑 Linux 下的 GNU tar, 那不是什么问题, 直接用 —exclude 就行了: tar cf - . --exclude temp | gzip -9 > ../test.tar.gz

当然, 在 Slackware 下没什么问题, 但是, 在 SunOS 和 FreeBSD 上都用不了, 错误提示如下:

SunOS: tar: --exclude: No such file or directory

FreeBSD: tar: --exclude: Cannot stat: No such file or directory

看来两者都把 —exclude 参数当作文件了, 因为它们都不识别这样的参数.

man 了一下, 看到了个 X 参数, SunOS 下的帮助截取了部分如下:

X    Exclude. Use the exclude-file argument as  a
     file  containing  a  list  of  relative path
     names  for  files  (or  directories)  to  be
     excluded  from  the  tarfile  when using the
     functions c, x, or t.

看来这个参数用了一个文件来指定不包含的文件/目录, 难道为了在打包的时候不包含一个目录, 我还要专门建立一个文件? 在不甘心的情况下, 用了下面的命令: echo ./temp | tar cXf - - . | gzip -9 > ../test.tar.gz

这个命令在 Slackware 下和 FreeBSD 下都没什么问题, 而在 SunOS 下却提示: tar: could not open -: No such file or directory

看来 SunOS 不支持用 – 来从标准输入获取内容作为 exclude-file 的内容, 于是想到了这样的命令: tar cXf <(echo temp) - . | gzip -9 > ../test.tar.gz

这下在三个系统中都能够正常运行了, 但是突然想到, <(echo temp) 这样的写法是 bash 支持的, 不知道老式的 sh 能不能正常, 在 SunOS 和 FreeBSD 中 /bin/sh 并不是指向 bash 的, Slackware 中有个 ash (听说 Slackware 安装脚本都是 ash 兼容的, 而没用 bash 的增强功能, 佩服:) ), 于是在这三个简单的 shell 下试了下, 果然, 三个都不行, 而且提示相近: Syntax error: `(' unexpected 只不过 SunOS 中 Syntax 的 ‘S’ 是小写的罢了

看来, 严格的说, 用 <(echo temp) 的方法, 也不是那么好的, 虽然说 bash 到处都是, 但是, 比如用 busybox 的 sh 的小型系统, 也许就没有 bash (不知道 busybox 的 sh 是不是支持这样的写法, 也不想再去专门安装个 busybox 来试了)

那看来, 要是想在一般的 shell ( bsh之类的? ) 中尽可能兼容多的 tar, 并且要不包含某些文件/目录, 那是需要一个临时的 exclude-file 了(要是谁知道怎么可以不用建这个临时的文件, 希望告之:) ): echo ./temp > ../exclude.lst && tar cXf ../exclude.lst - . | gzip -9 > ../test.tar.gz && rm -f ../exclude.lst

这时候想到, echo ./temp > ../exclude.lst 会直接覆盖 ../exclude.lst 文件, 当然, 存在这样名字文件的机率并不大, 但是既然想到了, 想想也无妨, 直接的想法是, 使用 mktemp , 这个命令应该也是比较常见的吧:) 于是, 上面的命令变成了:

TMP_EXCLUDE="`mktemp ../exclude.XXXXXX`" && echo ./temp > "$TMP_EXCLUDE" && tar cXf "$TMP_EXCLUDE" - . | gzip -9 > ../test.tar.gz && rm -f "$TMP_EXCLUDE"

真是麻烦啊 :(

接着 mktemp 往下想, 如果建立的不是临时文件, 而是临时目录呢?

顺手在 SunOS 里面 man mktemp 了一下, 看到了 -d 参数, 可以建立临时目录, 试了一下, 果然可以, 但是这次让人想不到的是, 这个参数在 SunOS 和 FreeBSD 都可以用, 唯独在 Slackware 里面不能用, 后来我查了一下, 是在 Slackware 9.1 里面不能用, 而之后的 Slackware 10 就可以用了, 之前的版本没有试过.

到 Slackware 的网站上看了看, 它用的 mktemp 是 debianutils 里面的, 早期的版本没有实现 mktemp 的 -d 参数, 使用这个参数会提示:

-d option is not supported under Linux

至于是不是每个 Linux 早期的发行版都不支持这个, 我就不知道了, 但是至少某些早期的 Linux 发行版不支持这个参数- -U

不过好在 mktemp 有个 -u 的参数, 勉强可以这样用:

TMP_DIR=`mktemp -u test.XXXXX` && mkdir "$TMP_DIR"

我想在 mktemp 删除了临时的文件之后, 其它进程恰好建立了这样名字的文件/目录的机会应该不是很大吧, 不过, mktemp 的手册上面写了, 这个参数是不安全的, 所以, 怎么用还是自己根据情况选择吧- -U

PS: 这篇东西纯粹是无聊给自己找麻烦玩的东西, 实际写脚本的时候, 也许根本就不用考虑这么多, 不过, 都说了, 无聊嘛, 找事玩的

另, 上网查了一下, 在 ChinaUnix Wiki 的相关条目中说到: “SunOS 5.10就叫做Solaris 10”, 所以本文里面说的 SunOS 就应该是 Solaris 10 了

另, 最近在 debian 中试了一下 busybox, 它的 mktemp 不支持 -u 参数, 所以, 又得把那个 mktemp 命令改成: TMP_DIR=`mktemp test.XXXXX` && rm -rf "$TMP_DIR" && mkdir "$TMP_DIR"

分类:

评论

  预览后可提交