官网:
    http://www.datsi.fi.upm.es/~frosal/sources/shc.html

compile.sh 比较特殊, 其使用说明中的代码, 容易被意外执行. 故而将整个说明放在这里.


:<<COMMENT
    使用 shc 命令, 以宿主文件的入口文件(内部可能 source 其他脚本)为起点, 编译成指定目标可执行文件. 

    说明:
        1. 必须在源文件(但不一定是入口文件, 可以是被 source 的脚本)内自调用(自编译), 无法编译第三方脚本, 
            因为生成独立文件的过程, 可能涉及到被 source 的脚本路径, 如果不是在自身进程内, 这些路径基本上
            无法(或难度相当大)定位.
        2. 最终生成的瘦身文件最后也添加了自编译代码(通过选项参数配置), 执行一次也可生成可执行文件, 
            主要用于简单解决目标文件的平台兼容性; 当然, 如果指定放弃自编译, 则瘦身文件仅仅作为一个无外部依赖
            的已打包子脚本在内的合成脚本.

    使用:
        1. 格式: 
            script2execution  --target-path(-t)   <="...">  \
                --attaches(-a)                  <=''> \
                --ignore-flag(-n)               <=Ignored> \
                --source-block-flag(-b)         <=Sourcing> \
                --iso(-i)                       <=.iso> \
                --slim(-s)                      <=.slm> \
                --plf(-f)                       <=.plf>
                --keep-flags(-k)                <=.keep_line>
                --cmdline-when-over(-l)         <=''>
                --preserve(-p)                  ?
                --give-up-self-compile(-g)      ?
        2. 参数很多, 但如果没有特殊定制(主要是区域名按照约定), 一两个足矣: 
            script2execution  --target-path ...
            script2execution  --target-path ... --attaches ...
        3. 与 perform 函数结合示例:
            1) perform 一次成型:
                perform --prompt "编译中, 请稍候......" -l \
                        "script2execution    
                            --target-path \"$ROOT_DIR_PATH/../../../execution/shc_usage/testing\" 
                            --attaches \"data/readme.txt  doc  no-such-fold-or-file 'some space/here en/cfg.json'   kkb\" 
                            --ignore-flag DonotCompileHere 
                            --source-block-flag SourcingCode
                            --iso .alone 
                            --slim .thin
                            --plf .plat 
                            --keep-flags \"keep_one keep-two keep-three keep_line\"
                            --cmdline-when-over keep-your-secret
                            --preserve
                        "   -d 0.3
            2) 分步进行:
                cmdline="script2execution    
                        --target-path \"$ROOT_DIR_PATH/../../../execution/shc_usage/testing\" 
                        --attaches \"data/readme.txt  doc  no-such-fold-or-file 'some space/here en/cfg.json'   kkb\" 
                        --ignore-flag DonotCompileHere 
                        --source-block-flag SourcingCode
                        --iso .alone 
                        --slim .thin
                        --plf .plat 
                        --keep-flags \"keep_one keep-two keep-three keep_line\"
                        --cmdline-when-over keep-your-secret
                        --preserve
                "
                perform -p "编译中, 请稍候......" -l "$cmdline" -d 0.1
            3) 最简化, 不带进度:
                script2execution  \
                        --target-path "$ROOT_DIR_PATH/../../../execution/shc_usage/testing" \
                        --attaches "data/readme.txt  doc  no-such-fold-or-file 'some space/here en/cfg.json'   kkb" 
            4) 最简化, 带进度:
                cmdline="script2execution 
                        --target-path \"$ROOT_DIR_PATH/../../../execution/shc_usage/testing\" 
                        --attaches \"data/readme.txt  doc  no-such-fold-or-file 'some space/here en/cfg.json'   kkb\" 
                    "
                perform -p "编译中, 请稍候......" -l "$cmdline" -d 0.2

    函数执行步骤: 
        1. 准备独立文件: 将所有 source 的脚本代码, 递归(因为子脚本可能又 source 了其他脚本)拷贝到源文件相同位置;
            1) 独立文件中已保证不包含各式 source 语句
            2) 独立文件中已保证不包含 script2execution 以及相应的 perform 语句.
        2. 生成瘦身文件, 即去掉所有注释, 为顺利编译做准备
        3. 编译瘦身文件, 保存为指定的目标可执行文件
        4. 注入自编译代码到上述瘦身文件中, 并将其移动至上述目标可执行文件同目录. 当上述目标可执行文件由于 linux
            发行版(或版本)差异运行失败, 可在该 OS 下, 运行该瘦身文件, 即可得到适应于该 OS 的可执行体. 注意,
            该瘦身文件此时被自动删除.
        5. 由于源文件的运行, 可能有相应的文件动态读写, 例如配置文件 config.json 之类, 故函数为此提供有复制选项

    参数: 
        -t, --target-path: 可执行文件路径(推荐无扩展名), 父目录不存在则创建. 必填

        -a, --attaches: 附件(文件/目录, 可以是 1 或多个)的相对路径, 相对于入口脚本文件所在目录. 默认: 空
            说明:
                这些文件/目录将被复制到目标文件所在目录的对应位置, 多个附件, 中间用空格分开. 每个文件和目录, 必要时
                (例如自身包含空格), 用单引号或者转义双引号包围.

        -n, --ignore-flag: 区域标志, 指示该区域内的代码, 不要写入目标独立文件. 默认: Ignored
            格式: 是否顶格可选; 末尾如果还有字串, 与标志之间至少需要一个空格; 区域内还可以夹杂注释, 只要不影响源文件执行
                # region Ignored
                    # 1. 直接调用
                    script2execution ... ..."

                    # 2. 同时显示进度的做法
                    cmdline="script2execution ... ...""
                    perform -p "编译中, 请稍候......" -l "\$cmdline"  -d 0.3
                #endregion Ignored
            说明:
                1. 用于标志区域, 该区域内的代码, 仅仅与编译相关, 生成独立文件时, 应忽略该区域的代码, 以免递归编译.     

        -b, --source-block-flag: 区域标志, 该区域内使用 find 命令, 配合 for 循环, 逐个 source 脚本. 默认: Sourcing
            格式与 ignore_flag 类似:
                #region Sourcing
                    # 1. 在一个循环内包含多个 find
                        for file in \
                            $(find \$ROOT_DIR_PATH/../../../library \
                            #    -path "some-absolute-dir" -prune -o    # 有了此项, 末尾必须加 -print
                                -type f -regex .*?\.sh$ \
                                -not -name 0_entry.sh \
                                -not -name options.sh) \
                            $(find $ROOT_DIR_PATH  \
                            -type f \
                            -regex .*\.sh \
                            -not -name "$ENTRY_FILE_NAME"\
                            -not -name "fat_$ENTRY_FILE_NAME"); do 
                        # 还可以夹杂注释, 只要不影响源文件执行
                                source $file
                                echo "source <$file>"
                        done
                        # 还可以夹杂注释, 只要不影响源文件执行
                    # 2. 一个循环内包含仅一个 find
                        for file in \
                            $(find $ROOT_DIR_PATH  \
                            -type f \
                            -regex .*?\.sh$ \
                            -not -name "*$ENTRY_FILE_NAME"); do 
                                source $file
                                echo "source <$file>"
                        # 还可以夹杂注释, 只要不影响源文件执行
                        done
                #endregion Sourcing
            说明:
                函数解析出每个 for 循环内的所有 find 语句并执行, 以获取所有被 source 的脚本, 然后逐个展开, 递归写入
                目标独立文件.

        -i, --iso: 即 isolated file extension 独立文件的扩展名, 默认为 .iso 
            说明:
                1. 独立文件保存于源文件同目录
                2. 该扩展名作用, 在于当脚本重入时, for 循环配合 find 命令的 source 脚本, 通过扩展名不同, 可避开
                    上次生成的独立文件. 当然如果 find 没有过滤 .sh 脚本扩展名, 或者指定 -i 为 .sh, 我们认为文件
                    的爆炸式增长, 是用户有意而为之. 
                3. 函数将该扩展名衔接到源文件名末尾, 例如:
                    源文件 xyz.sh, -i 赋值 .iso, 则对应独立文件 xyz.sh.iso

        -s, --slim: 即 slim file extension 瘦身文件的扩展名, 默认为 .slm
            说明:
                1. 瘦身文件是去除入口脚本独立文件内所有无用行(包括空行, 注释行), 得到的文件, 与源文件和独立文件同目录
                2. 该扩展名作用, 与 iso 类似.
                3. 函数将该扩展名衔接到独立文件名末尾, 例如:
                    上述源文件 xyz.sh, --iso=.iso --slm=.slm, 则瘦身文件为 xyz.sh.iso.slm

        -f, --plf: 对应于某 linux 发行版(某版本)的可执行体的扩展名. 默认为 .plf
            说明:
                有时生成的可执行体在不同的 linux 发行版(以及同一发行版的不同版本)有可能无法运行, 故在独立的瘦身脚本文件
                (例如上面的 xyz.sh.iso.slm)末尾, 添加了自编译代码, 该代码作用:
                    1. 生成绝对可在当前平台上运行的可执行体, 该可执行体扩展名由 --plf 设置;
                    2. 删除自身, 例如上面的 xyz.sh.iso.slm.

        -k, --keep-flags:   行首带此字符串包含的标志之一, 表示强制保持该行(去掉 # 与字符串标志后). 
            多个标志用空格隔开, 所以一个单独标志无法包含空格, 否则被当成多个标志. 默认: keep_line.  
            说明:
                1. 脚本要自己保证该标志不影响脚本执行,  # 号的前后可以有 0 或多个空格. 例如, 调用
                        local spt=$(new Scripter --keep-flags preserved_comment \
                            --source-path ./<path>/<filename>.sh) ...
                    则对于以下注释
                            #preserved_comment # comment1
                                #  preserved_comment   # comment2
                                    #    preserved_comment # comment3
                    甚至下面的注释也合法, 虽然可读性不好:
                            #preserved_comment#comment4
                    以上四种情况, 得到的代码行分别是(在行内的具体起始位置, 由标志去除后决定):
                    # comment1 和 # comment2 和 # comment3 和 #comment4 

        -l, --cmdline-when-over: 每次运行完， 需要脚本执行的命令行。主要用于扰乱、清空配置文件。

        -p, --preserve: 标旗参数, 表示是否持久保存整个过程产生的中间文件, 出现表示保存, 否则表示不保存(用后立即删除)
            说明: 
                可指定删除的文件包括
                    1. 与入口脚本相关的所有脚本以及最终的入口脚本对应的独立文件(包含注释) .iso
                    2. shc 编译命令产生的 c 源文件: .x.c
                当然, 目标独立瘦身文件 xyz.sh.iso.slm的保留或删除不受此限制, 参见 --plf 参数说明
            
        -g, --give-up-self-compile 标旗参数, 表示是否放弃在最终生成的瘦身文件最后添加自编译代码, 出现表示放弃,
            否则表示添加.
COMMENT

附:
    1. 上述自编译代码注入函数, 早期配置为 echo 和 cat 版本, 均为自获取路径的方式. 但由于 compile.sh 的
        script2execution 函数, 目前只适用于当前进程的入口脚本的编译任务, 故已废弃. 作为备份, 内容如下:
            # 将 shc 自编译代码追加到指定脚本的最后. 使用 echo 写, 
            # 稍微繁琐了些, 因为 $ 和 " 都需要转义
            # 参数:
            #   $1: 瘦身文件路径
            #   $2: 用作瘦身文件运行后, 由其自编译代码生成的可执行体的扩展名(包含 . )
            _inject_self_compile2(){
                log_info "starting echo self compile code for <$1> ....."
                echo "
                    # 为消除与入口文件处定义的全局变量的依赖, 在此重新求值
                    _cur_dir=\$(dirname \$(readlink -f \$0))
                    # 可执行文件运行时, 下面的变量得到空字符, 故可以此保证只编译 shell 脚本.
                    _cur_file_name=\${BASH_SOURCE[0]##*/}
                    _cur_file_path=\"\$_cur_dir/\$_cur_file_name\"
                    _exe_file_name=\"\${_cur_file_name%%.*}$2\"  # 适应平台的可执行文件
                    _exe_file_path=\"\$_cur_dir/\$_exe_file_name\"
                    # 可执行文件运行时, _cur_file_path 得到目录而不是文件
                    if [  -f \"\$_cur_file_path\" ]; then
                        if ! type shc 2>/dev/null; then
                            tip \"shc is required, please wait a moment...\"
                            func=apt
                            if ! type apt 2>/dev/null; then
                                func=yum
                                yum clean all
                            fi
                            \$func update -y
                            \$func install shc -y
                        fi
                        aop tip center \"now compile <\$_cur_file_path> to <\$_exe_file_path>, is ok?\"
                        shc -rU -f \"\$_cur_file_path\" -o \"\$_exe_file_path\"
                        rm -f \"\$_cur_file_path\"  \"\$_cur_dir\"/*.x.c
                    fi
                    " >> "$1"
                log_info "finished!"
            }

            # 将 shc 自编译代码追加到指定脚本的最后. 使用 cat 写
            # 参数:
            #   $1: 瘦身文件路径
            #   $2: 用作瘦身文件运行后, 由其自编译代码生成的可执行体的扩展名(包含 . )
            _inject_self_compile1(){
                log_info "starting cat self compile code fro <$1>....."
                cat >> "$1" <<- EOF
                    declare -g plf=$2
                EOF # 此处 EOF 必须顶格至最左边

                # 这里如果强求 EOF 对齐 cat, 以便折叠代码, 可使用下面的带先导空格的标志:
                # cat >> $slim_file <<- '        EOF        '
                # 可惜这会带来使得后续的代码着色错误的缺点(至少 vscode 是这样), 所以还是改成常规写法
                cat >> "$1" <<- 'EOF'
                    # 为消除与入口文件处定义的全局变量的依赖, 在此重新求值
                    _cur_dir=$(dirname $(readlink -f $0))
                    # 可执行文件运行时, 下面的变量得到空字符, 故可以此保证只编译 shell 脚本.
                    _cur_file_name=${BASH_SOURCE[0]##*/}
                    _cur_file_path="$_cur_dir/$_cur_file_name"
                    _exe_file_name="${_cur_file_name%%.*}$plf"  # 适应平台的可执行文件
                    _exe_file_path="$_cur_dir/$_exe_file_name"
                    # 可执行文件运行时, _cur_file_path 得到目录而不是文件
                    if [  -f "$_cur_file_path" ]; then
                        if ! type shc 2>/dev/null; then
                            tip "shc is required, please wait a moment..."
                            func=apt
                            if ! type apt 2>/dev/null; then
                                func=yum
                                yum clean all
                            fi
                            $func update -y
                            $func install shc -y
                        fi
                        aop tip center "now compile <$_cur_file_path> to <$_exe_file_path>, is ok?"
                        shc -rU -f "$_cur_file_path" -o "$_exe_file_path"
                        rm -f "$_cur_file_path"  "$_cur_dir"/*.x.c
                    fi
                EOF # 此处 EOF 必须顶格至最左边
                log_info "finished!"
            } 


            # 将 shc 自编译代码追加到指定脚本的最后. 使用 cat 写
            # 参数:
            #   $1: 瘦身文件路径
            #   $2: 用作瘦身文件运行后, 由其自编译代码生成的可执行体的扩展名(包含 . )
            # 说明: 由于 script2execution 函数只能用于当前进程的入口脚本的编译, 所以当前文件/目录的
            #       获取, 直接饮用脚本起始的 ROOT_DIR_PATH 等等即可
            _inject_self_compile3(){
                log_info "starting cat self compile code fro <$1>....."

                cat >> "$1" <<- EOF
                    declare -g plf=$2
                EOF # 此处 EOF 必须顶格至最左边
                cat >> "$1" <<- 'EOF'
                    declare -g _exe_file_path="$ROOT_DIR_PATH/${ENTRY_FILE_NAME%%.*}$plf"  # 适应平台的可执行文件
                    # 可执行文件运行时, _cur_file_path 得到目录而不是文件
                    if [ ! -f "$_exe_file_path" ]; then
                        if ! type shc 2>/dev/null; then
                            tip "shc is required, please wait a moment..."
                            func=apt
                            if ! type apt 2>/dev/null; then
                                func=yum
                                yum clean all
                            fi
                            $func update -y
                            $func install shc -y
                        fi
                        # 即时编译为适应当前平台的可执行文件
                        shc -rU -f "$ENTRY_FILE_PATH" -o "$_exe_file_path"
                        rm -f "$ENTRY_FILE_PATH"  "$ROOT_DIR_PATH"/*.x.c
                    fi
                EOF # 此处 EOF 必须顶格至最左边
                log_info "finished!"
            } 

▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
create_slim:
    去除注释. 可用作静态和实例方法
    参数:
        -s, --source-path   要瘦身的 .sh 脚本文件, 默认为当前进程的入口脚本, 即 $(readlink -f $0)

        -t, --target-path   生成的文件. 为空则表示打印到控制台. 默认为空

        -k, --keep-flags:   行首带此字符串包含的标志之一, 表示强制保持该行(去掉 # 与字符串标志后). 
                        多个标志用空格隔开, 所以一个单独标志无法包含空格, 否则被当成多个标志. 默认: keep_line.  
                        说明:
                            1. 脚本要自己保证该标志不影响脚本执行,  # 号的前后可以有 0 或多个空格. 例如, 调用
                                    local spt=$(new Scripter --keep-flags preserved_comment \
                                        --source-path ./<path>/<filename>.sh) ...
                                则对于以下注释
                                        #preserved_comment # comment1
                                            #  preserved_comment   # comment2
                                                #    preserved_comment # comment3
                                甚至下面的注释也合法, 虽然可读性不好:
                                        #preserved_comment#comment4
                                以上四种情况, 得到的代码行分别是(在行内的具体起始位置, 由标志去除后决定):
                                # comment1 和 # comment2 和 # comment3 和 #comment4 

        -a, --append-remark 成功后, 是否在瘦身文件末尾添加瘦身说明. 标旗参数, 即出现(不论赋予何值)表示添加; 不出现表示不添加

    用法(园括号内是对应的短选项):
        create_slim --source-path(-s)   <=self_entry> \
                    --target-path(-t)   <=''>
                    --keep-flag(-k)     <='keep_line'> \
                    --append-remark(-a) ?

    保持默认值的简单使用(静态调用方式, 仅打印到控制台):
        Scripter.create_slim    

▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
compile_single_file:
    编译单一文件, 不管内部是否 source 其他文件
    --source(-s):   要编译的文件路径
    --exts(-e):     为了与源文件区别开来, 将以此为扩展名(可以是多扩展名, 例如 .one.two.three 等等)
    --clean(-c):    标旗参数. 指示编译完成是否清除源文件和编译产生的中间文件
    返回:
        1. source 文件如果不存在, 则操作被忽略, 并返回 1;
        2. exts 扩展名为空, 或者与 source 的扩展名相同, 或者是 .x.c, 则操作被忽略, 并返回 2;
        3. 成功注入后, 返回 0.
    用法: compile_single_file   --source(-s) <path> \
                                --exts(-e)   <extention> \
                                --clean(-c)  ?
    注意:
        1. 函数不对 source 的内容做语法检查, 仅仅是将自编译代码注入(追加到最后). 
        2. 运行时发现 OS 为安装 shc, 则自动安装完毕, 然后继续编译
        3. 目标文件位于源文件同目录.
        4. 编译完成后, 源文件和中间文件(*.x.c)是否被删除, 取决于命令行是否 clean(或 c) 参数.