我试图编写一个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:标准定:文件意外结尾tar:儿童返回状态 1tar:错误不可恢复:现在退出
期望的输出
在大纲中,要生成的脚本应如下所示:
base64 -d <<'EOF' | tar -xzf -
…base-64 encoded data…
EOF
base64
命令解码其标准输入,该输入作为 here 文档提供,以仅包含 EOF
的行结尾。 输出将写入 tar
具有提取从标准输入读取的 gzip 数据的选项。
最小脚本
因此,最小的生成器脚本如下所示:
echo "base64 -d <<'EOF' | tar -czf -"
tar -czf - "$@" | base64 -w 72
echo "EOF"
这回显了base64 … | tar …
行,然后使用 tar
在标准输出上生成一个压缩的 tar 文件,其中包含命令行上命名的文件或目录,输出通过管道传输到 GNU coreutils 版本的base64
,并可以选择指定输出行应为 72 个字符宽(加上换行符)。 接下来是EOF
,以标记此处文档的结尾。
您可以将 shebang 行 ( #!/bin/sh
) 添加到其中一个或两个脚本中。 无需选择更具体的外壳;这只使用核心 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
(对于相应的字母)以及用法。 似乎没有办法从中获取版本信息。 但是,GNU 的base64
确实会在您请求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 不会尝试将标记文本或存档内容作为代码执行。 如果您愿意,您可以将提取代码保留为压缩程度较低的格式,但最终必须为存档部分创建一个临时文件并确保将其删除。