自解压 tar 存档(shell 脚本)

问题描述 投票:0回答:3

我尝试编写一个 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:错误不可恢复:立即退出

shell unix scripting tar self-extracting
3个回答
2
投票

所需输出

概括地说,您要生成的脚本应如下所示:

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
并进行相应调整。您需要在生成器脚本中进行一个测试,并在生成的脚本中需要一份测试的副本。这绝对是一个更精致的程序。


1
投票

有必要自己做吗?有一个名为 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 编码的情况下完成此操作。

0
投票
您只需在文件中添加一个小标头,该标头在运行时会将其自身从文件中剥离并将其传递给 tar。与使用 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

这种方法有一些复杂之处,即固定的跳跃值。除非标头本身发生更改,否则它不会引起问题。
    

© www.soinside.com 2019 - 2024. All rights reserved.