我尝试编写一个 shell 脚本来创建另一个以 base64 压缩和编码的自解压 tar 存档。我不知道该从哪里开始,而且在 shell 脚本方面几乎没有经验。
这个脚本创建了压缩和编码的 tar 存档,但是当我尝试从终端运行
./tarName
时,自解压不起作用。任何建议表示赞赏
#!/bin/sh
tarName=$1;
if [ -e $tarName.tar.gz ]
then /bin/echo "$tarName already exists"
exit 0
fi
shift;
for files;
do
tar -czvf tmpTarBall.tar.gz $files;
done
echo "#!/bin/sh" >> $tarName.tar.gz;
echo "base64 -d $tarName.tar.gz" >> $tarName.tar.gz;
echo "tar -xzvf $tarName.tar.gz" >> $tarName.tar.gz;
chmod +x ./$tarName.tar.gz;
base64 tmpTarBall.tar.gz >> $tarName.tar.gz;
rm tmpTarBall.tar.gz;
----------更新
环顾四周,这就是我现在拥有的,但仍然不起作用。谁能帮我解释一下为什么吗?
#!/bin/sh
tarName=$1;
if [ -e $tarName.tar.gz ]
then /bin/echo "$tarName already exists"
exit 0
fi
shift;
for files;
do
tar -czvf tmpTarBall.tar.gz $files;
done
cat > extract.sh;
echo "#!/bin/sh" >> extract.sh;
echo "sed '0,/^#TARBALL#$/d' $0 | $tarName.tar.gz | base64 -d | tar -xzv; exit 0" >> extract.sh;
echo "#TARBALL#" >> extract.sh;
cat extract.sh tmpTarBall.tar.gz > $tarName.tar.gz;
chmod +x ./$tarName.tar.gz;
rm extract.sh tmpTarBall.tar.gz;
当我尝试运行 tarName.tar.gz 时,出现错误: ./tarName.tar.gz: 2: ./tarName.tar.gz: tarName.tar.gz: 未找到 gzip:stdin:意外的文件结尾 tar: 孩子返回状态 1 tar:错误不可恢复:立即退出
概括地说,您要生成的脚本应如下所示:
base64 -d <<'EOF' | tar -xzf -
…base-64 encoded data…
EOF
base64
命令解码其标准输入,该输入作为此处文档提供,并以仅包含EOF
的行结尾。输出被写入
tar
具有提取从标准输入读取的 gzip 压缩数据的选项。
因此,最小的生成器脚本如下所示:
echo "base64 -d <<'EOF' | tar -czf -"
tar -czf - "$@" | base64 -w 72
echo "EOF"
这与
base64 … | tar …
行相呼应,然后使用 tar
在标准输出上生成一个压缩的 tar 文件,其中包含命令行上指定的文件或目录,并且输出通过管道传输到 base64
的 GNU coreutils 版本选项指定输出行的宽度应为 72 个字符(加上换行符)。这一切后面都有 EOF
来标记此处文档的结尾。
您可以向其中一个或两个脚本添加 shebang 行 (
#!/bin/sh
)。无需选择更具体的 shell;这仅使用核心 shell 脚本结构,这些结构可以追溯到很久以前——在 POSIX 成为人们眼中的闪光之前。
可能出现的问题包括对 Mac OS X
base64
的支持,其中包含如下使用消息:
Usage: base64 [-dhvD] [-b num] [-i in_file] [-o out_file]
-h, --help display this message
-D, --decode decodes input
-b, --break break encoded string into num character lines
-i, --input input file (default: "-" for stdin)
-o, --output output file (default: "-" for stdout)
-v
选项和-d
选项都会生成base64: invalid option -- v
(对于适当的字母),以及用法。似乎没有办法从中获取版本信息。然而,当您请求 base64
时,GNU 的 base64 --version
确实会生成有用的消息。标准输出的第一行将包含类似以下内容:
base64 (GNU coreutils) 8.22
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Simon Josefsson.
这被写入标准输出。因此,您可以自动检测您是否拥有 GNU
base64
并进行相应调整。您需要在生成器脚本中进行一个测试,并在生成的脚本中需要一份测试的副本。这绝对是一个更精致的程序。
有必要自己做吗?有一个名为 makeself 的现有工具可以为您完成此操作。如果您确实需要自己写这个,这里有一些想法:
您的输出文件是一个存档,其前面粘贴有 shell 脚本。提取过程通过 base64
和
tar
运行整个输出文件,而不仅仅是存档。
base64
调用将脚本部分变成垃圾,从而使
tar
感到困惑。您需要做的是添加一些代码,将脚本与存档分开,然后仅在存档部分运行其余命令。一种可能的方法是将提取脚本调整为如下所示:
#!/bin/sh
linenum=$(grep -n "__END_OF_SCRIPT_MARKER__" $tarName.tar.gz | tail -1 | sed -e 's/:.*//')
tail -n +$(($linenum + 1)) $tarName.tar.gz | base64 -d | tar -xzv
exit 0
__END_OF_SCRIPT_MARKER__
确保标记文本后面的脚本部分中除了换行符(该网站上的标记不显示换行符)外没有任何内容。这样,您可以使用
grep
查找包含标记的行号,然后使用 tail
删除那么多行。剩下的将是存档部分,它通常由代码的其余部分处理。
exit
行确保 shell 不会尝试将标记文本或存档内容作为代码执行。如果您愿意,可以将提取代码保留为压缩程度较低的格式,但最终您必须为存档部分创建一个临时文件并确保将其删除。有一种方法可以在不使用 Base64 编码的情况下完成此操作。
这是我能做的最好的事情,并且仍然可以正常运行:
dd if=$0 bs=4 skip=13 2>/dev/null | tar xavf -
exit
...raw tar file...
如果您愿意,可以在开头添加 shebang。
#!/bin/sh
dd if=$0 bs=2 skip=31 2>/dev/null | tar xavf -
exit
...raw tar file...
仅供参考,-a 选项用于压缩自动检测。要实际使用该代码片段,请运行此命令
cat headerfile data.tar.gz > data.tar.gz.self
这种方法有一些复杂之处,即固定的跳跃值。除非标头本身发生更改,否则它不会引起问题。