Бывает необходимо приложить к сообщению небольшой бинарный файл там, где такой встроенной возможности нет (хотя бы как здесь, на blogger.com).
Один из вариантов - архиватор Shar из комплекта GNU Sharutils.
Для дополнительного сжатия файлов с помощью gzip архиватор следует запускать командой вроде:
shar -z {каталог или файл для архивирования} > ./archive.shar
В итоге получится файл archive.shar, являющийся по сути скриптом, который достаточно просто запустить для распаковки и получения исходных данных.
Содержание файла чисто текстовое, поэтому его содержание можно смело вставлять в сообщения, e-mail или др. средства текстовой связи. Если сайт попытается найти и обработать html-синтаксис в тексте shar-скрипта, побив его тем самым (как это делает тот же blogger), необходимо сначала сконвертить shar-текст скриптом txt2html.
Расплатой за удобство служит необходимость наличия sharutils на принимающей стороне (что в общем-то справедливо и в случае использования любого другого архиватора), а так же gzip, если использовалось дополнительное сжатие (опция -z).
Для примера прикладываю скрипт-архив со вложенными директориями, тестовым и бинарным (png-изображение) файлами.
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.4).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 10 -rw-r--r-- TEST/test.txt
# 0 -rw-r--r-- TEST/empty
# 0 -rw-r--r-- TEST/test_dir/2nd_empty
# 781 -rw-r--r-- TEST/gnuplot.png
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
if test "$gettext_dir" = FAILED && test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir ;;
esac
fi
if test "$locale_dir" = FAILED && test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
echo=echo
else
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
shar_n= shar_c='
'
else
shar_n=-n shar_c=
fi
else
shar_n= shar_c='\c'
fi
if touch -am -t 200112312359.59 $$.touch >/dev/null 2>&1 && test ! -f 200112312359.59 && test -f $$.touch; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am 123123592001.59 $$.touch >/dev/null 2>&1 && test ! -f 123123592001.59 && test ! -f 123123592001.5 && test -f $$.touch; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am 1231235901 $$.touch >/dev/null 2>&1 && test ! -f 1231235901 && test -f $$.touch; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
$echo 'WARNING: not restoring timestamps. Consider getting and'
$echo "installing GNU \`touch', distributed in GNU File Utilities..."
echo
fi
rm -f 200112312359.59 123123592001.59 123123592001.5 1231235901 $$.touch
#
$echo $shar_n 'x -' 'lock directory' "\`_sh09789': "$shar_c
if mkdir _sh09789; then
$echo 'created'
else
$echo 'failed to create'
exit 1
fi
# ============= TEST/test.txt ==============
if test ! -d 'TEST'; then
$echo $echo_n 'x -' 'TEST: '$echo_c
if mkdir 'TEST'; then $echo 'created'; else $echo 'failed to create'; fi
fi
if test -f 'TEST/test.txt' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'TEST/test.txt' '(file already exists)'
else
$echo 'x -' extracting 'TEST/test.txt' '(gzipped)'
sed 's/^X//' << 'SHAR_EOF' | uudecode &&
begin 600 _sh09789/gzi
>'XL(`)A8T4<"`RO.STU5*$FM*.$"`+SF+!D*````
`
end
SHAR_EOF
$echo 'gunzipping file' 'TEST/test.txt' &&
gzip -d < _sh09789/gzi > 'TEST/test.txt' &&
(set 20 08 03 07 18 00 40 'TEST/test.txt'; eval "$shar_touch") &&
chmod 0644 'TEST/test.txt' ||
$echo 'restore of' 'TEST/test.txt' 'failed'
if ( md5sum --help </dev/null 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version </dev/null 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'TEST/test.txt:' 'MD5 check failed'
4d93d51945b88325c213640ef59fc50b TEST/test.txt
SHAR_EOF
else
shar_count="`LC_ALL=C wc -c < 'TEST/test.txt'`"
test 10 -eq "$shar_count" ||
$echo 'TEST/test.txt:' 'original size' '10,' 'current size' "$shar_count!"
fi
fi
# ============= TEST/empty ==============
if test ! -d 'TEST'; then
$echo $echo_n 'x -' 'TEST: '$echo_c
if mkdir 'TEST'; then $echo 'created'; else $echo 'failed to create'; fi
fi
if test -f 'TEST/empty' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'TEST/empty' '(file already exists)'
else
$echo 'x -' extracting 'TEST/empty' '(empty)'
> 'TEST/empty' &&
(set 20 08 03 07 18 00 28 'TEST/empty'; eval "$shar_touch") &&
chmod 0644 'TEST/empty' ||
$echo 'restore of' 'TEST/empty' 'failed'
if ( md5sum --help </dev/null 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version </dev/null 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'TEST/empty:' 'MD5 check failed'
d41d8cd98f00b204e9800998ecf8427e TEST/empty
SHAR_EOF
else
shar_count="`LC_ALL=C wc -c < 'TEST/empty'`"
test 0 -eq "$shar_count" ||
$echo 'TEST/empty:' 'original size' '0,' 'current size' "$shar_count!"
fi
fi
# ============= TEST/test_dir/2nd_empty ==============
if test ! -d 'TEST/test_dir'; then
$echo $echo_n 'x -' 'TEST/test_dir: '$echo_c
if mkdir 'TEST/test_dir'; then $echo 'created'; else $echo 'failed to create'; fi
fi
if test -f 'TEST/test_dir/2nd_empty' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'TEST/test_dir/2nd_empty' '(file already exists)'
else
$echo 'x -' extracting 'TEST/test_dir/2nd_empty' '(empty)'
> 'TEST/test_dir/2nd_empty' &&
(set 20 08 03 07 18 00 28 'TEST/test_dir/2nd_empty'; eval "$shar_touch") &&
chmod 0644 'TEST/test_dir/2nd_empty' ||
$echo 'restore of' 'TEST/test_dir/2nd_empty' 'failed'
if ( md5sum --help </dev/null 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version </dev/null 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'TEST/test_dir/2nd_empty:' 'MD5 check failed'
d41d8cd98f00b204e9800998ecf8427e TEST/test_dir/2nd_empty
SHAR_EOF
else
shar_count="`LC_ALL=C wc -c < 'TEST/test_dir/2nd_empty'`"
test 0 -eq "$shar_count" ||
$echo 'TEST/test_dir/2nd_empty:' 'original size' '0,' 'current size' "$shar_count!"
fi
fi
# ============= TEST/gnuplot.png ==============
if test -f 'TEST/gnuplot.png' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'TEST/gnuplot.png' '(file already exists)'
else
$echo 'x -' extracting 'TEST/gnuplot.png' '(gzipped)'
sed 's/^X//' << 'SHAR_EOF' | uudecode &&
begin 600 _sh09789/gzi
M'XL(`/;86CP"`P$-`_+\B5!.1PT*&@H````-24A$4@```"`````@"`,```!$
MI(K&```!(%!,5$4\/#I+-C53(R-60D-5555J8%]B8F%\:FIZ>GJ!7UZ*;&RU
M4U*U5UBB:&>J;6RK<W*R<W2R>GN\=7.[=WB]?W_%>WO(>GK;<7':?WZW?X"Z
M@'^&AH:0D)"=G)RGEYBDF9FJEI>U@H*Y@X.]CY&YD(Z[DY2MJZRXN+C!AX;$
MC8W)C8W%DY+,EI;.G)S4AH;6BXK5FYK+GZ#5G:#-IZ;+J*;2I*;:HJ';K:W:
ML:_5O;[;L[/<O+SRBHG\G)WGIJ3BJ*?JHZ/HJ*CCL*_@MK?FNKOKM+3LO;WP
MHJ#UL;#RNKK_OK[$Q,3+P<'EP<'JRLOPR,C]P\/_R\KST]']TM/]W-O\W^'_
MX-__X^/_Z.;_[>W_[_'_\.__]?3_]_G_^/?^_?UX6LLN```!J$E$051XVG72
M"4_",!0'\'IBO!`#\<!A.-0L,C5JHH@B48P':B8K8[/OO7W_;V';;3(&:]*D
M77][:?\M"]*-B*_O[#H43=DT@*>NY]:(L@"**@!\W0%F`&ARDLT<90`<-7V2
M&^G8-!L`MU1QZIS#3("B<`?R7^+;&<`O@CH`^9L]+=@W3`+'"C^`L^6I$2N*
M"8&7/U$<HLA#X%,2P'X\)]-5!V5G;K("PKZ(@5?3X-5.`K@:Q%,2!0VZ=I2I
M7O?W8%RLR17HY%M+?=F693_)/6^TCE?DR"CW/UM'C=9GG]4;N;E#PS"8[`<+
MF_E*>5&.5I8-H[(ZWS@TF(IVO$?[!E(G8L&P.3XHO+\D`#6X`F`.Z7]?54BD
M0MP,-.#_;^EZ,`'<.DB`S@7%!:Q!,E9T32!YF[\6#Y-`[]1+`A(6EP#AN1V!
MD3EQ+[)B3\@*V.Z%89*;!D[>4P\&]WP-L"K2[Z_TH$%!`_R^@13`CWL-7M[5
M,X5>FRA5P5ECX>Y_D0AJ(K4>!'Z)Q0$%-*I-K0<PT$!&[`/4W6!&8]$EO7GV
G+60#Y.>E1S$3_`&C4U;\WYN)V0````!)14Y$KD)@@NW>I6\-`P``
`
end
SHAR_EOF
$echo 'gunzipping file' 'TEST/gnuplot.png' &&
gzip -d < _sh09789/gzi > 'TEST/gnuplot.png' &&
(set 20 02 02 01 21 05 42 'TEST/gnuplot.png'; eval "$shar_touch") &&
chmod 0644 'TEST/gnuplot.png' ||
$echo 'restore of' 'TEST/gnuplot.png' 'failed'
if ( md5sum --help </dev/null 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version </dev/null 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'TEST/gnuplot.png:' 'MD5 check failed'
afe5ff1db943f6bfad21b568e79047e9 TEST/gnuplot.png
SHAR_EOF
else
shar_count="`LC_ALL=C wc -c < 'TEST/gnuplot.png'`"
test 781 -eq "$shar_count" ||
$echo 'TEST/gnuplot.png:' 'original size' '781,' 'current size' "$shar_count!"
fi
fi
$echo $shar_n 'x -' 'lock directory' "\`_sh09789': " $shar_c
if rm -fr _sh09789; then
$echo 'removed'
else
$echo 'failed to remove'
fi
exit 0
Если бы конкретно (по шагам) написали, как пользоваться архиватором, то статья имела бы успех.
> Если бы конкретно (по шагам) написали,
> как пользоваться архиватором,
> то статья имела бы успех.
Всё есть в этой весьма скромной статье.
Для запаковки нужен единственный несложный шаг:
shar -z {каталог или файл для архивирования} > ./archive.shar
Для распаковки достаточно запустить получившийся файл как обычный shell-скрипт.