sh shell 中的自解压脚本



我将如何制作可以在sh上执行的自解压存档?

我最接近的是:

extract_archive () {
    printf '<archive_contents>' | tar -C "$extract_dir" -xvf -
}

其中<archive_contents>包含带有空字符的压缩包,%'字符转义并括在单引号之间。

有没有更好的方法来做到这一点,这样就不需要逃跑了?

(请不要指出我sharmakeself等。我想从头开始写它。

另一种变体是使用标记作为外壳脚本的结尾,并使用 sed 来剪切外壳脚本本身。

脚本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

一些关于如何做到这一点的好文章可以在以下位置找到:

  • http://www.linuxjournal.com/node/1005818。
  • https://community.linuxmint.com/tutorial/view/1998

是的,您可以使用 xtar 本机执行此操作。

  1. 构建xtar elf64 tar 自解压器头(您可以自由修改它以支持 elf32、pe 和其他可执行格式),它基于轻量级的 bsdtar untar 和 std elf lib。

    cc contrib/xtar.c -o ./xtar
    
  2. 将二进制xtar复制到yourTar.xtar

    cp ./xtar yourTar.xtar
    
  3. 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 '/':
        temp="${current_path%?}"
        while [ "${current_path#"$temp"}" = "/" ]; do
            current_path="${current_path%?}"
            temp="${current_path%?}"
        done
        first_path_component="${current_path%"/"*}"
        if [ -z "$first_path_component" ]; then
            last_path_component="${current_path#'/'}"
        else
            last_path_component="${current_path#"$first_path_component""/"}"
        fi
    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%sn' "Parameter $cc: '$current_param_func'"
        done
        
        if [ -n "$find_parameters" ]; then
            printf 'n%snn' "Find parameters: $find_parameters"
        fi
    fi
    }>&2
}
PrintInTitle () {
    printf "33]0;%s07" "$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 "33]0;%s07" "">"$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 '%sn' "Press Enter to exit...">"$print_to_screen"
        read temp
    fi
}
DisplayHelp () {
    printf '%sn' " 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 '%sn' " "
    printf '%sn' " Archiving:"
    printf '%sn' "     - Parameters syntax: <input_folder_path> [ <archive_name> <output_folder_path> ] [ <flags> ]"
    printf '%sn' "     "
    printf '%sn' "     - what it does:"
    printf '%sn' "         - archives <input_folder> as <archive_name>.<default_extension> in the specified <output_folder_path>"
    printf '%sn' "             - if <archive_name> is an empty string ("") / empty: it is considered the name of the <input_folder>"
    printf '%sn' "             - if <output_folder_path> is an empty string ("") / empty: it is considered the path of the parent directory of <input_folder>"
    printf '%sn' "         - for archiving: <flags> can be:"
    printf '%sn' "             - --text-mode"
    printf '%sn' "                 - archive only text files"
    printf '%sn' "             - --find-parameters / -f"
    printf '%sn' "                 - all the parameters given after the '--find-parameters' flag, are considered 'find' parameters (denoted here as <find_parameters>)"
    printf '%sn' "                 - <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 '%sn' "     - usage:"
    printf '%sn' "     "
    printf '%sn' " Extraction:"
    printf '%sn' "     - Parameters syntax: <input_archive_path> [ <extraction_output_name> <extraction_output_path> ] [ <flags> ]"
    printf '%sn' "     "
    printf '%sn' "     - what it does:"
    printf '%sn' "         - extracts <input_archive> to the <extraction_output_path>/<extraction_output_name> path; if non-empty: <extraction_output_name> is created"
    printf '%sn' "             - 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 '%sn' "             - if <extraction_output_path> is an empty string ("") / empty / not provided: it is considered the path of the parent directory of <input_archive>"
    printf '%sn' " "
    printf '%sn' " General <flags> can be:"
    printf '%sn' "     -h / --help - displays this help information"
    printf '%sn' "     -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
        man -f $utility >/dev/null 2>/dev/null || { ErrorMessage "ERROR: the '$utility' utility is not installed!"; }
    done>&2
    
    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%sn' "Archiving: Please provide the ouput archive name (default = Enter = archive's parent directory name):"
    ReadRaw archive_name
    
    printf '%sn' "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 '%sn' "Archiving: Please provide find parameters (default = Enter = <none>):"
        ReadRaw find_parameters
    fi
    }>$print_to_screen
}
ErrorMessage () {
    if [ "$error" = "false" ]; then
        PrintErrorExtra
    fi
    printf 'n%sn' "$1">&2
    error="true"
}
PrintFileStart () {
    printf "n"
    printf '%sn' "### >>> START: FILE $k ($1):"
    printf "tn"
    printf 't%sn' "current_error="false";"
    printf "tn"
}
PrintFileEnd () {
    printf 't%sn' "[ "$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 '%sn' "### <<< END: FILE $k ($1):"
    printf "n"
}
PrintExtractingFileMessage () {
    printf 't%sn' "PrintJustInTitle "Extracting file $k - size $current_file_size_in_bytes"" B...""
    printf 't%sn' "[ "$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' "nn") #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="TEXTARCHIVE"

#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 "%snn" "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 "nn">&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 '%sn' "### $ARCHIVE_SIGNATURE_STRING ARCHIVE ###"
    
    cat<<'EOF'
### >>> START: ARCHIVE HEADER
PrintInTitle () {
    printf "33]0;%s07" "$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
        man -f $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 '%sn' "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 '%sn' "Press Enter to exit...">"$print_to_screen"
        read temp
    fi
}
ReadExtractParameters () {
    {
    printf 'n%sn' "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 '%sn' "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%sn' "$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%sn' "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 ! -path '.' -a ! -path './' -a ! -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 '%sn' "$current_file_or_dir2"|sed "s/'/$Q"$Q"$Q/g")"
    
    if [ ! -r "$current_file_or_dir" ]; then
        printf '%sn' "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 '%sn' "Archiving file $j (dir): "$current_file_or_dir2"..."
        fi
        
        k=$(($k+1))
        
        { {
            PrintFileStart "DIR"
            
            printf "t%sn" "crt_file_or_dir_path_escaped='$crt_file_or_dir_path2_escaped'"
            printf "tn"
            
            PrintExtractingFileMessage dir
            
            printf 't%sn' "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 '%sn' "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%sn" "crt_file_or_dir_path_escaped='$crt_file_or_dir_path2_escaped'"
                printf "tn"
                
                PrintExtractingFileMessage file
                
                printf 't%sn' "mkdir -p "$BASE_PATH/${crt_file_or_dir_path_escaped%"/"*}" && {"
                printf 'tt%sn' "{"
                printf 'ttt%sn' "{"
                
                printf 'tttt%sn' "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%sn' 'EOFxxyyzzzzyyxx'
                printf 'ttt%sn' "}|sed 's/eEOFxxyyzzzzyyxx/EOFxxyyzzzzyyxx/g'>>"$BASE_PATH/$crt_file_or_dir_path_escaped""
                printf 'tt%s' "} 2>/dev/null"
                
                printf '%sn' " && {"
                printf 'ttt%sn' "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 'tt%sn' "} || current_error="true""
                
                if [ "$utf" = "true" ]; then
                    printf 'tt%sn' "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%sn' "} || 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%snn" "crt_file_or_dir_path_escaped='$crt_file_or_dir_path2_escaped'"
                
                PrintExtractingFileMessage file
                
                printf 't%sn' "mkdir -p "$BASE_PATH/${crt_file_or_dir_path_escaped%"/"*}" && {"
                
                printf '%sn' "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 "}x3ex3ex22$BASE_PATH/$crt_file_or_dir_path_escaped"".tempxyzzyxx22"; 
                                print "{ catx3cx3cx27EOFxxyyzzzzyyxxx27"; 
                            }; 
                        '
                    }
                fi
                
                printf '%sn' "EOFxxyyzzzzyyxx"
                printf "t%s" "}>>"$BASE_PATH/$crt_file_or_dir_path_escaped"".tempxyzzyx""
                printf '%sn' " || 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 'tt%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%sn' "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%snn' "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 '%sn' " Errors were encountered!"
    elif [ "$error" = "false" ]; then
        printf '%sn' " 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 '%sn' " Errors were encountered!"
elif [ "$error" = "false" ]; then
    printf '%sn' " No errors were encountered!"
fi
printf "n"
CleanUp

最新更新