我将如何制作一个可以在
sh
上执行的自解压存档?
我最接近的是:
extract_archive () {
printf '<archive_contents>' | tar -C "$extract_dir" -xvf -
}
其中
<archive_contents>
包含带有空字符的 tarball,%
、'
和 \
字符已转义并用单引号括起来。
有没有更好的方法来做到这一点,这样就不需要转义?
(请不要指给我
shar
,makeself
等,我想从头开始写。)
替代方案是使用 shell 脚本结尾标记并使用 sed 剪切 shell 脚本本身。
脚本
selfextract.sh
:
#!/bin/bash
sed '0,/^#EOF#$/d' $0 | tar zx; exit 0
#EOF#
使用方法:
# create sfx
cat selfextract.sh data.tar.gz >example_sfx.sh
# unpack sfx
bash example_sfx.sh
由于 shell 脚本不是编译的,而是逐条执行的,因此您可以使用如下模式混合二进制和文本内容(未经测试):
#!/bin/sh
sed -e '1,/^exit$/d' "$0" | tar -C "${1-.}" -zxvf -
exit
<binary tar gzipped content here>
您可以将这两行添加到几乎任何 tar+gzip 文件的顶部,以使其可自解压。
测试:
$ cat header.sh
#!/bin/sh
sed -e '1,/^exit$/d' "$0" | tar -C "${1-.}" -zxvf -
exit
$ tar -czf header.tgz header.sh
$ cat header.sh header.tgz > header.tgz.sh
$ sh header.tgz.sh
header.sh
有关如何做到这一点的一些好文章可以在以下位置找到:
是的,您可以使用
xtar
在本地完成此操作。
xtar
elf64 tar 自解压头(您可以自由修改以支持 elf32、pe 等可执行格式),它基于轻量级 bsdtar untar 和 std elf lib。
cc contrib/xtar.c -o ./xtar
将
xtar
二进制文件复制到 yourTar.xtar
cp ./xtar yourTar.xtar
将
yourTar.tar
存档附加到 yourTar.xtar
的末尾
cat yourTar.tar >> yourTar.xtar
chmod +x yourTar.xtar
这里有一个
dash
脚本(应该在 Linux 和 Mac OS-es 上开箱即用),可以创建自解压 shell 档案(您可以使用 --help
标志调用它以了解如何使用它):
#!/bin/dash
ExtractFirstAndLastPathComponent () {
eval current_path="\"\$$1\""
first_path_component=""
last_path_component=""
if [ -n "$current_path" ]; then
#Remove trailing '/' characters:
while [ ! "${current_path%"/"}" = "$current_path" ]; do
current_path="${current_path%"/"}"
done
if [ -z "$current_path" ]; then
eval current_path=\"\$$1\"
fi
last_path_component="${current_path##*"/"}"
first_path_component="${current_path%"$last_path_component"}"
fi
eval $2="\"\$first_path_component\""
eval $3="\"\$last_path_component\""
}
ConvertToFullPath () {
initial_dir_ctfp="$PWD"
eval current_path="\"\$$1\""
lpc_current_path=""
ExtractFirstAndLastPathComponent current_path fpc_current_path lpc_current_path
if [ -n "$fpc_current_path" ]; then
if [ -d "$fpc_current_path" ]; then
cd "$fpc_current_path"
fpc_current_path="$PWD"
fi
fi
if [ -n "$lpc_current_path" ]; then
eval $2="\"\$fpc_current_path/\$lpc_current_path\""
else
eval $2=\"\/\"
fi
cd "$initial_dir_ctfp"
}
GetFileEncodingAndSizeInBytes () {
eval file_to_test=\"\$$1\"
GetFileSizeInBytes file_to_test file_to_test_size_in_bytes
#Get file mime encoding:
if [ -d "$file_to_test" ]; then
result="directory"
file_to_test_size_in_bytes="0"
elif [ ! "$file_to_test_size_in_bytes" -eq "0" ]; then
file_mime_type="$(file -bL --mime-encoding "$file_to_test" 2>/dev/null)" || { file_mime_type="undetermined"; }
case "$file_mime_type" in
*"binary"* )
#Only binary files containing the NULL character (^@) are considered binaries in this script:
(cat -v "$file_to_test" | sed "/\^@/i'\^@'\$NL2") | { grep -q "\^@"; } && result="binary" || result="ascii"
;;
*"ascii"* )
result="ascii"
;;
*"utf"* )
result="${file_mime_type##*" "}"
;;
* )
result="undetermined"
;;
esac
else
result="ascii"
fi
eval $2=\"\$result\"
eval $3=\"\$file_to_test_size_in_bytes\"
}
GetOSType () {
case "$(uname -s)" in
*"Darwin"* | *"BSD"* )
eval $1="BSD-based"
;;
*"Linux"* )
eval $1="Linux"
;;
* )
eval $1="Other"
;;
esac
}
GetFileSizeInBytes () {
eval file="\"\$$1\""
[ -z "$OS_TYPE" ] && GetOSType OS_TYPE
if [ "$OS_TYPE" = "BSD-based" ]; then
file_size_in_bytes="$(stat -Lf %z -- "$file")" 2>/dev/null || { file_size_in_bytes="-1"; }
elif [ "$OS_TYPE" = "Linux" ] || [ "$OS_TYPE" = "Other" ]; then
file_size_in_bytes="$(stat -c %s -- "$file")" 2>/dev/null || { file_size_in_bytes="-1"; }
fi
eval $2="$file_size_in_bytes"
}
PrintErrorExtra () {
{
if [ ! "$error" = "true" ]; then
for cc in $(seq 1 $params_0); do
eval current_param_func=\"\$params_$cc\"
printf '\n%s\n' "Parameter $cc: '$current_param_func'"
done
if [ -n "$find_parameters" ]; then
printf '\n%s\n\n' "Find parameters: $find_parameters"
fi
fi
}>&2
}
PrintInTitle () {
printf "\033]0;%s\007" "$1"
}
PrintJustInTitle () {
PrintInTitle "$1">"$print_to_screen"
}
trap1 () {
printf "\n""Archiving: Aborted.\n">"$print_to_screen"
CleanUp
#kill all children processes, suppressing "Terminated" message:
kill -s PIPE -- -$$ 2>/dev/null
exit
}
CleanUp () {
#Restore "INTERRUPT" (CTRL-C) and "TERMINAL STOP" (CTRL-Z) signals:
trap - INT
trap - TSTP
#Clear the title:
printf "\033]0;%s\007" "">"$print_to_screen"
#Restore initial IFS:
unset IFS
#Restore initial directory:
cd "$initial_dir"
CheckPause
}
CheckPause () {
#For not running from command line: pause the script after finishing:
if [ "$params_0" = "1" ] && [ "$flag_count" -eq "0" ]; then
printf '%s\n' "Press Enter to exit...">"$print_to_screen"
read temp
fi
}
DisplayHelp () {
printf '%s\n' " TEXT project ARCHiver - a dash shell based script to archive a folder and its content as a self extracting SHell script ARchive (SHAR) (plain text)"
printf '%s\n' " "
printf '%s\n' " Archiving:"
printf '%s\n' " - Parameters syntax: <input_folder_path> [ <archive_name> <output_folder_path> ] [ <flags> ]"
printf '%s\n' " "
printf '%s\n' " - what it does:"
printf '%s\n' " - archives <input_folder> as <archive_name>.<default_extension> in the specified <output_folder_path>"
printf '%s\n' " - if <archive_name> is an empty string (\"\") / empty: it is considered the name of the <input_folder>"
printf '%s\n' " - if <output_folder_path> is an empty string (\"\") / empty: it is considered the path of the parent directory of <input_folder>"
printf '%s\n' " - for archiving: <flags> can be:"
printf '%s\n' " - --text-mode"
printf '%s\n' " - archive only text files"
printf '%s\n' " - --find-parameters / -f"
printf '%s\n' " - all the parameters given after the '--find-parameters' flag, are considered 'find' parameters (denoted here as <find_parameters>)"
printf '%s\n' " - <find_parameters> can be: any parameters that can be passed to the 'find' utility (which is used internally by this script) - such as: name/path filters"
printf '%s\n' " - usage:"
printf '%s\n' " "
printf '%s\n' " Extraction:"
printf '%s\n' " - Parameters syntax: <input_archive_path> [ <extraction_output_name> <extraction_output_path> ] [ <flags> ]"
printf '%s\n' " "
printf '%s\n' " - what it does:"
printf '%s\n' " - extracts <input_archive> to the <extraction_output_path>/<extraction_output_name> path; if non-empty: <extraction_output_name> is created"
printf '%s\n' " - if <extraction_output_name>: is empty/not provided -> it is considered blank; is an empty string (\"\") -> it is considered the name of the <input_archive>"
printf '%s\n' " - if <extraction_output_path> is an empty string (\"\") / empty / not provided: it is considered the path of the parent directory of <input_archive>"
printf '%s\n' " "
printf '%s\n' " General <flags> can be:"
printf '%s\n' " -h / --help - displays this help information"
printf '%s\n' " -v / --verbose - displays the current file progress on the terminal screen (by default only errors are displayed)"
printf "\n"
}
CheckUtilities () {
#Check if any of the necessary utilities is missing:
error="false"
for utility; do
which $utility>/dev/null 2>/dev/null || { printf "\n%s\n" "ERROR: the '$utility' utility is not installed!">&2; error="true"; }
done
if [ "$error" = "true" ]; then
CleanUp; exit 1
fi
}
ReadRaw () {
initial_IFS="$IFS"
IFS= read -r $1
#Restore initial IFS:
unset IFS
eval value=\"\$$1\"
if [ -n "$value" ]; then printf "\n"; fi
}
ReadArchiveParameters () {
{
printf '\n%s\n' "Archiving: Please provide the ouput archive name (default = Enter = archive's parent directory name):"
ReadRaw archive_name
printf '%s\n' "Archiving: Please provide the output archive folder path (default = Enter = the parent folder path of the archive):"
ReadRaw output_path
if [ -z "$find_parameters" ]; then
printf '%s\n' "Archiving: Please provide find parameters (default = Enter = <none>):"
ReadRaw find_parameters
fi
}>$print_to_screen
}
ErrorMessage () {
if [ "$error" = "false" ]; then
PrintErrorExtra
fi
printf '\n%s\n' "$1">&2
error="true"
}
PrintFileStart () {
printf "\n"
printf '%s\n' "### >>> START: FILE $k ($1):"
printf "\t\n"
printf '\t%s\n' "current_error=\"false\";"
printf "\t\n"
}
PrintFileEnd () {
printf '\t%s\n' "[ \"\$current_error\" = \"true\" ] && { error=\"true\"; printf \"ERROR: Extraction: FILE $k: Could not write to: \\\"\$BASE_PATH/\$crt_file_or_dir_path_escaped\\\"\"; }>&2"
printf "\n"
printf '%s\n' "### <<< END: FILE $k ($1):"
printf "\n"
}
PrintExtractingFileMessage () {
printf '\t%s\n' "PrintJustInTitle \"Extracting file $k - size $current_file_size_in_bytes"" B...\""
printf '\t%s\n' "[ \"\$verbose_flag\" = \"1\" ] && printf \"%s\\n\" \"Extracting file $k ($1): \\\"\$BASE_PATH/\$crt_file_or_dir_path_escaped\\\" - size: $current_file_size_in_bytes Bytes...\">\$print_to_screen"
}
print_to_screen="/dev/tty" #print to screen only
initial_dir="$PWD"
NL2=$(printf '%s' "\n\n") #Store New Line for use with sed
#Trap "INTERRUPT" (CTRL-C) and "TERMINAL STOP" (CTRL-Z) signals:
trap 'trap1' INT
trap 'trap1' TSTP
#For use with xxd: max number of lines stored at a time by the 'cat' utility:
MAX_LINES_NUMBER=500000 #approx. 30 MB
#The maximum amount of memory in bytes, that the 'cat' utility can use, when archiving a text file as plain text:
MAX_TEXT_FILE_SIZE_IN_BYTES="2000000000" #approx. 2 GB
#Define archive extension here:
archive_extension="textarch"
#Enable globbing (POSIX compliant):
set +f
eval ARCHIVE_SIGNATURE_STRING=\"\T\E\X\T\A\R\C\H\I\V\E\"
GetOSType OS_TYPE
if [ "$OS_TYPE" = "Linux" ] || [ "$OS_TYPE" = "Other" ]; then
NEG='!'
elif [ "$OS_TYPE" = "BSD-based" ]; then
NEG='-not'
fi
#Store and process parameters:
help_flag="0"
verbose_flag="0"
text_flag="0"
find_parameters_flag="0"
params=""
find_parameters=""
selected_params_count=0
flag_count="0"
for param; do
if [ "$find_parameters_flag" = "0" ]; then
case "$param" in
"-v" | "--verbose" | "--help" | "--text-mode" | "-t" | "--find-parameters" | "-f" )
case "$param" in
"-v" | "--verbose" )
verbose_flag="1"
;;
"--help" )
help_flag="1"
;;
"--text-mode" | "-t" )
text_flag="1"
;;
"--find-parameters" | "-f" )
find_parameters_flag="1"
;;
esac
flag_count=$(($flag_count + 1))
;;
* )
selected_params_count=$(($selected_params_count+1))
eval selected_params_$selected_params_count=\"\$param\"
;;
esac
else
if [ "$find_parameters_flag" = "1" ]; then
if [ -z "$find_parameters" ]; then
find_parameters="$param"
else
if [ ! "$param" = "${param%*"*"*}" ] || [ ! "$param" = "${param%*"?"*}" ]; then # * and ? characters are treated literaly
find_parameters="$find_parameters ""'""$param""'"
else
find_parameters="$find_parameters ""$param"
fi
fi
fi
fi
done
selected_params_0="$selected_params_count"
if [ -z "$find_parameters" ]; then
find_parameters='-name "*"'
fi
if [ "$help_flag" = "1" ] || [ "$selected_params_0" = "0" ]; then
DisplayHelp
exit 0
fi
for i in $(seq 1 $selected_params_0); do
eval params_$i=\"\$selected_params_$i\" #Store parameters other than flags
done
params_0=$selected_params_0
CheckUtilities find sed cat file sort chmod grep mkdir iconv stat seq kill rm
#Process parameters/flags and check for errors:
eval find /dev/null $find_parameters>/dev/null || {
PrintErrorExtra
printf "%s\n\n" "ERROR: Invalid find parameters provided after the \"--find-parameters\" flag!">&2
CleanUp; exit 1
}
if [ "$params_0" -ge "1" ]; then
{
error="false"
if [ "$params_0" -gt "3" ]; then
ErrorMessage "ERROR: Archiving: Expected 1 or 3 parameters: <input_folder_path> [ <archive_name> <output_folder_path> ]!"
else
input_folder_path="$params_1"
if [ "$params_0" -eq "3" ]; then
archive_name="$params_2"
output_path="$params_3"
elif [ "$params_0" -eq "2" ]; then
ErrorMessage "ERROR: Archiving: Expected 1 or 3 parameters: <input_folder_path> [ <archive_name> <output_folder_path> ]!"
elif [ "$params_0" -eq "1" ]; then
ReadArchiveParameters
fi
if [ -n "$archive_name" ] && [ ! "${archive_name%*"/"*}" = "$archive_name" ]; then
ErrorMessage "ERROR: Archiving: Second provided non-flag parameter must be the name of the archive (must not contain '/')!"
fi
if [ -n "$output_path" ] && [ "${output_path%*"/"*}" = "$output_path" ]; then
ErrorMessage "ERROR: Archiving: Third provided non-flag parameter must be the output path of the archive (must either be blank or contain '/')!"
fi
fi
if [ "$error" = "true" ]; then
CleanUp; exit 1
fi
ConvertToFullPath input_folder_path input_folder_full_path
ExtractFirstAndLastPathComponent input_folder_full_path fpc_input_folder_path lpc_input_folder_path
if [ -z "$archive_name" ] || [ "$archive_name" = "\"\"" ]; then
archive_name="$lpc_input_folder_path"
fi
if [ -z "$output_path" ]; then
output_path="$fpc_input_folder_path"
if [ -z "$fpc_input_folder_path" ]; then
output_path='/'
fi
fi
archive_name_plus_ext="$archive_name"."$archive_extension"
if [ ! "$input_folder_full_path" = "/" ]; then
input_folder_parent_dir_path="$fpc_input_folder_path"
fi
if [ ! -d "$input_folder_path" ]; then
ErrorMessage "ERROR: Archiving: The path provided as the first non-flag parameter: \"$input_folder_path\" is not a valid directory path or is not accessible!"
fi
if [ ! -d "$output_path" ]; then
ErrorMessage "ERROR: Archiving: Output path: \"$output_path\" is not a valid directory path or is not accessible!"
fi
if [ "$error" = "true" ]; then
CleanUp; exit 1
fi
}>&2
ConvertToFullPath output_path output_path
#Remove trailing '/' from output_path:
ExtractFirstAndLastPathComponent output_path fpc_output_path lpc_output_path
if [ ! "$output_path" = '/' ]; then
output_path="$fpc_output_path/$lpc_output_path"
fi
cd "$initial_dir"
elif [ "$params_0" -eq "0" ]; then
DisplayHelp
exit 0
fi
params_0=$selected_params_0
if [ "$error" = "true" ]; then
printf "\n\n">&2
CleanUp; exit 1
fi
cd "$initial_dir"
### DEFINE OUTPUT FILE: ###
if [ "$output_path" = '/' ]; then
output_file="/$archive_name_plus_ext"
else
output_file="$output_path/$archive_name_plus_ext"
fi
if [ -e "$output_file" ]; then
ErrorMessage "ERROR: Archiving: Output file: \"$output_file\" already exists!"
CleanUp; exit 1
fi
ConvertToFullPath output_file output_file_full_path
###
{ cat /dev/null>>"$output_file_full_path"; } 2>/dev/null || {
ErrorMessage "ERROR: Archiving: Could not write to output file: \"$output_file\"!"
CleanUp; exit 1
}
{ {
ExtractFirstAndLastPathComponent input_folder_full_path fpc_input_folder_full_path lpc_input_folder_full_path
cat <<'EOF'
#!/bin/dash
EOF
printf '%s\n' "### $ARCHIVE_SIGNATURE_STRING ARCHIVE ###"
cat<<'EOF'
### >>> START: ARCHIVE HEADER
PrintInTitle () {
printf "\033]0;%s\007" "$1"
}
PrintJustInTitle () {
PrintInTitle "$1">"$print_to_screen"
}
ReadRaw () {
initial_IFS="$IFS"
IFS= read -r $1
#Restore initial IFS:
unset IFS
}
CheckUtilities () {
#Check if any of the necessary utilities is missing:
error="false"
for utility; do
which $utility >/dev/null 2>/dev/null || { ErrorMessage "ERROR: the '$utility' utility is not installed!"; }
done>&2
if [ "$error" = "true" ]; then
CleanUp2; exit 1
fi
}
trap2 () {
CleanUp2
printf "\n""Extraction: Aborted.\n">"$print_to_screen"
printf '%s\n' "Press Enter to exit...">"$print_to_screen"
read temp
#kill all children processes, suppressing "Terminated" message:
kill -s PIPE -- -$$ 2>/dev/null
}
CleanUp2 () {
#Restore "INTERRUPT" (CTRL-C) and "TERMINAL STOP" (CTRL-Z) signals:
trap - INT
trap - TSTP
cd "$initial_dir"
CheckPause
}
CheckPause () {
if [ -z "$params_1" ] && [ -z "$params_2" ]; then
printf '%s\n' "Press Enter to exit...">"$print_to_screen"
read temp
fi
}
ReadExtractParameters () {
{
printf '\n%s\n' "Extraction: Please provide the name of the folder where to extract the archive (this will be created) (default = blank = <empty>):"
ReadRaw extraction_output_name
if [ -n "$extraction_output_name" ]; then printf "\n"; fi
printf '%s\n' "Extraction: Please provide the parent directory path of the folder where to extract the archive (default = blank = <archive parent directory path>):"
ReadRaw extraction_output_path
if [ -n "$extraction_output_path" ]; then printf "\n"; fi
}>$print_to_screen
}
ErrorMessage () {
if [ "$error" = "false" ]; then
PrintErrorExtra
fi
printf '\n%s\n' "$1">&2
error="true"
}
PrintErrorExtra () {
{
if [ ! "$error" = "true" ]; then
for cc in $(seq 1 $params_0); do
eval current_param_func=\"\$params_$cc\"
printf '\n%s\n' "Parameter $cc: '$current_param_func'"
done
printf "\n"
fi
}>&2
}
ArchivingOutputFileWriteError () {
ErrorMessage "ERROR: Archiving: Could not write to output file: \"$output_file\"!"
CleanUp2; exit 1
}
print_to_screen="/dev/tty"
initial_dir="$PWD"
#Trap "INTERRUPT" (CTRL-C) and "TERMINAL STOP" (CTRL-Z) signals:
trap 'trap2' INT
trap 'trap2' TSTP
stored_archive_size_in_bytes="000000000000000"
verbose_flag="0"
i=0
params_0=0
for param; do
case "$param" in
"-v" | "--verbose" )
verbose_flag="1"
;;
* )
i=$(($i+1))
eval params_$i=\"\$param\"
;;
esac
done
params_0="$i"
CheckUtilities mkdir iconv sed rm
cd "${0%/*}" 2>/dev/null
current_script_path="$(pwd -P)/${0##*/}"
input_archive_path="$current_script_path"
cd "$initial_dir"
lpc_input_archive_path="${input_archive_path##*"/"}"
fpc_input_archive_path="${input_archive_path%"$lpc_input_archive_path"*}"
{
[ ! "$params_1" = "${params_1%*"/"*}" ] && params_1_is_full_path="false"
[ ! "$params_2" = "${params_2%*"/"*}" ] && params_2_is_full_path="false"
if [ ! "$params_0" -eq "0" ]; then
if [ "$params_0" -le "2" ]; then
if [ "$params_1_is_full_path" = "$params_2_is_full_path" ]; then
ErrorMessage "ERROR: Extraction: If 2 parameters provided: one should be a name (should not contain '/') and the other should be a path (should contain '/')!"
CleanUp2; exit 1
elif [ "$params_1_is_full_path" = "false" ]; then
extraction_output_name="$params_1"
extraction_output_path="$params_2"
else
extraction_output_name="$params_2"
extraction_output_path="$params_1"
fi
else
ErrorMessage "ERROR: Extraction: Expected between 0 and 2 parameters!"
CleanUp2; exit 1
fi
else
ReadExtractParameters
fi
if [ "$extraction_output_name" = \"\" ]; then
extraction_output_name="${lpc_input_archive_path%"."*}"
fi
if [ "$extraction_output_path" = "\"\"" ] || [ -z "$extraction_output_path" ]; then
extraction_output_path="$fpc_input_archive_path"
fi
error="false"
if [ -n "$extraction_output_path" ]; then
cd "$extraction_output_path" 2>/dev/null && {
if [ -e "$extraction_output_name" ]; then
ErrorMessage "ERROR: Extraction: The provided folder name: \"$extraction_output_name\" already exists as a directory in the specified location: \"$extraction_output_path\"!"
CleanUp2; exit 1
elif [ -n "$extraction_output_name" ]; then
mkdir "$extraction_output_name" 2>/dev/null || {
ErrorMessage "ERROR: Extraction: Could not create directory: \"$extraction_output_name\" to the extraction output path: \"$extraction_output_path\"!"
CleanUp2; exit 1
}
cd "$extraction_output_name"
fi
} || {
ErrorMessage "ERROR: Extraction: Could not access extraction output path: \"$extraction_output_path\"!"
CleanUp2; exit 1
}
fi
BASE_PATH="$PWD"
if [ ! -w "$BASE_PATH" ]; then
ErrorMessage "ERROR: Extraction: Cannot extract archive: BASE_PATH directory \"$BASE_PATH\" is not writable\"!"
CleanUp2; exit 1
fi
}
EOF
}>"$output_file"; } 2>/dev/null || ArchivingOutputFileWriteError
ExtractFirstAndLastPathComponent output_file_full_path fpc_output_file_full_path lpc_output_file_full_path
ExtractFirstAndLastPathComponent input_folder_full_path fpc_input_folder_full_path lpc_input_folder_full_path
if [ "$verbose_flag" = "1" ]; then
printf "\n"
fi
if [ ! "$input_folder_full_path" = "/" ]; then
if [ "$fpc_input_folder_full_path" = "" ]; then
input_folder_name="/$lpc_input_folder_full_path"
else
input_folder_name="./$lpc_input_folder_full_path"
fi
else
input_folder_name=""
fi
{ {
cat<<EOF
if [ -z "\$extraction_output_name" ] && [ -e "$input_folder_name" ]; then
ErrorMessage "ERROR: Extraction: Could not create directory: \"$input_folder_name\" to the extraction output path: \"\$extraction_output_path\" - directory already exists!"
CleanUp2; exit 1
fi
error="false"
Q="'"
EOF
}>>"$output_file"; } 2>/dev/null || ArchivingOutputFileWriteError
Q="'"
IFS='
'
j=0
k=0
error="false"
{
cat<<'EOF'
cd "$BASE_PATH"
### <<< END: ARCHIVE HEADER
EOF
}>>"$output_file_full_path"
if [ "$verbose_flag" = "1" ]; then
printf "\n"
fi
cd "$initial_dir"
if [ "$error" = "true" ]; then printf "\n">&2 ; fi
#Proceed to Archiving files:
cd "$input_folder_full_path"
output_file_relative_path=".${output_file_full_path#$input_folder_full_path}"
j=0
k=0
for line in $(eval find . \\\( -type d -o -type f \\\) -a $NEG -path '.' -a $NEG -path './' -a $NEG -path "\"\$output_file_relative_path\"" $find_parameters 2>/dev/null|sort); do
j=$(($j+1))
current_file_or_dir="$input_folder_parent_dir_path/$input_folder_name/$line"
GetFileEncodingAndSizeInBytes current_file_or_dir current_file_encoding current_file_size_in_bytes
current_file_size_in_bytes_padded="$(printf '%012d' "$current_file_size_in_bytes")"
PrintJustInTitle "Analyzing/Archiving: file path $j - size $current_file_size_in_bytes_padded"" B..."
current_file_or_dir2="$input_folder_name/$line"
crt_file_or_dir_path2_escaped="$(printf '%s\n' "$current_file_or_dir2"|sed "s/'/$Q\"\$Q\"$Q/g")"
if [ ! -r "$current_file_or_dir" ]; then
printf '%s\n' "ERROR: Archiving: File not accessible/readable: \"$current_file_or_dir2\"!">&2
error="true"
fi
if [ -d "$current_file_or_dir" ]; then
if [ "$verbose_flag" = "1" ]; then
printf '%s\n' "Archiving file $j (dir): \"$current_file_or_dir2\"..."
fi
k=$(($k+1))
{ {
PrintFileStart "DIR"
printf "\t%s\n" "crt_file_or_dir_path_escaped='$crt_file_or_dir_path2_escaped'"
printf "\t\n"
PrintExtractingFileMessage dir
printf '\t%s\n' "mkdir -p \"\$BASE_PATH/\$crt_file_or_dir_path_escaped\" 2>/dev/null || { current_error=\"true\"; }"
PrintFileEnd "DIR"
}>>"$output_file_full_path"; } 2>/dev/null || ArchivingOutputFileWriteError
elif [ -f "$current_file_or_dir" ]; then
if [ "$verbose_flag" = "1" ]; then
printf '%s\n' "Archiving file $j (file): \"$current_file_or_dir2\" - size: $current_file_size_in_bytes Bytes..."
fi
if [ ! "$current_file_encoding" = "binary" ] && [ "$current_file_size_in_bytes" -le "$MAX_TEXT_FILE_SIZE_IN_BYTES" ]; then
k=$(($k+1))
{ {
PrintFileStart "TEXT FILE"
printf "\t%s\n" "crt_file_or_dir_path_escaped='$crt_file_or_dir_path2_escaped'"
printf "\t\n"
PrintExtractingFileMessage file
printf '\t%s\n' "mkdir -p \"\$BASE_PATH/\${crt_file_or_dir_path_escaped%\"/\"*}\" && {"
printf '\t\t%s\n' "{"
printf '\t\t\t%s\n' "{"
printf '\t\t\t\t%s\n' "cat<<'EOFxxyyzzzzyyxx'"
utf8="false"; utf="false"
case "$current_file_encoding" in
*"utf-8"* )
utf8="true"
;;
*"utf"* )
utf="true"
;;
esac
if [ "$utf8" = "true" ]; then
cat "$current_file_or_dir"
elif [ "$utf" = "true" ]; then
iconv -f "$current_file_encoding" -t utf8 "$current_file_or_dir"
else
cat "$current_file_or_dir"
fi|sed 's/EOFxxyyzzzzyyxx/eEOFxxyyzzzzyyxx/g'
printf '\n%s\n' 'EOFxxyyzzzzyyxx'
printf '\t\t\t%s\n' "}|sed 's/eEOFxxyyzzzzyyxx/EOFxxyyzzzzyyxx/g'>>\"\$BASE_PATH/\$crt_file_or_dir_path_escaped\""
printf '\t\t%s' "} 2>/dev/null"
printf '%s\n' " && {"
printf '\t\t\t%s\n' "perl -pi -e 'chomp if eof' \"\$BASE_PATH/\$crt_file_or_dir_path_escaped\" 2>/dev/null || sed -i -z 's/\(\n\)$//' \"\$BASE_PATH/\$crt_file_or_dir_path_escaped\" 2>/dev/null"
printf '\t\t%s\n' "} || current_error=\"true\""
if [ "$utf" = "true" ]; then
printf '\t\t%s\n' "iconv -f utf-8 -t \"$current_file_encoding\" \"\$BASE_PATH/\$crt_file_or_dir_path_escaped\" -o \"\$BASE_PATH/\$crt_file_or_dir_path_escaped\""
fi
printf '\t%s\n' "} || current_error=\"true\""
PrintFileEnd "TEXT FILE"
}>>"$output_file_full_path"; } 2>/dev/null || ArchivingOutputFileWriteError
elif [ "$text_flag" = "0" ]; then
k=$(($k+1))
{
PrintFileStart "HEX ENCODED FILE"
printf "\t%s\n\n" "crt_file_or_dir_path_escaped='$crt_file_or_dir_path2_escaped'"
PrintExtractingFileMessage file
printf '\t%s\n' "mkdir -p \"\$BASE_PATH/\${crt_file_or_dir_path_escaped%\"/\"*}\" && {"
printf '%s\n' "cat<<'EOFxxyyzzzzyyxx'"
if [ "$current_file_size_in_bytes" -le "100000000000" ]; then
xxd -p "$current_file_or_dir"|{
awk '\
{ print $0; }; \
(NR%'"$MAX_LINES_NUMBER"'==0){ \
print "EOFxxyyzzzzyyxx"; \
print "}\x3e\x3e\x22$BASE_PATH/$crt_file_or_dir_path_escaped"".tempxyzzyx\x22"; \
print "{ cat\x3c\x3c\x27EOFxxyyzzzzyyxx\x27"; \
}; \
'
}
fi
printf '%s\n' "EOFxxyyzzzzyyxx"
printf "\t%s" "}>>\"\$BASE_PATH/\$crt_file_or_dir_path_escaped"".tempxyzzyx\""
printf '%s\n' " || current_error=\"true\""
printf '\t%s'"\n" "xxd -p -r \"\$BASE_PATH/\$crt_file_or_dir_path_escaped.tempxyzzyx\" \"\$BASE_PATH/\$crt_file_or_dir_path_escaped\" && {"
printf '\t\t%s'"\n" "rm \"\$BASE_PATH/\$crt_file_or_dir_path_escaped.tempxyzzyx\""
printf '\t%s'"\n" "}"
PrintFileEnd "HEX ENCODED FILE"
}>>"$output_file_full_path" || ArchivingOutputFileWriteError
elif [ "$current_file_encoding" = "binary" ]; then
printf '\n%s\n' "WARNING: Archiving: Skipping binary file: \"$current_file_or_dir2\"">&2
fi
fi
done
if [ ! "$file_or_dir_path_0" = "0" ]; then
if [ "$verbose_flag" = "1" ]; then printf "\n"; fi
else
printf '\n%s\n\n' "WARNING: Archiving: No file match - so no files where added to the archive...">&2
fi
{
cat<<EOF
### >>> START: ARCHIVE FOOTER
### TOTAL FILES COUNT: $k
PrintJustInTitle "Extraction: Done."
printf '\n%s' "Extraction: Done."
if [ "\$error" = "true" ]; then
printf '%s\n' " Errors were encountered!"
elif [ "\$error" = "false" ]; then
printf '%s\n' " No errors were encountered!"
fi
CleanUp2
### <<< END: ARCHIVE FOOTER
EOF
}>>"$output_file_full_path"
#Make the generated archive a read-only file for all users:
chmod a=r "$output_file"
#Make the generated archive an executable file for all users:
chmod a+x "$output_file"
PrintJustInTitle "Archiving: Done."
printf "\n"
printf '%s' "Archiving: Done."
if [ "$error" = "true" ]; then
printf '%s\n' " Errors were encountered!"
elif [ "$error" = "false" ]; then
printf '%s\n' " No errors were encountered!"
fi
printf "\n"
CleanUp