如何设计可重用的脚本处理其他脚本的临时文件生命周期

如何设计可重用的脚本处理其他脚本的临时文件生命周期

我需要创建一个可重用(“实用程序”)脚本,为我的任何其他(“应用程序”)脚本处理使用临时文件的整套操作:

  1. 创建
  2. 跟踪创建的临时文件
  3. 捕获退出(用户定期完成和中断)并删除创建的所有内容

每个应用程序脚本都需要自己“包含”实用程序脚本,而不是依赖某些“主”脚本会为其使用的所有下标调用一次它。

关键的要求是使用实用程序脚本的应用程序脚本可以互相调用,它们中的每一个又需要使用临时文件。脚本之间的调用都是

./some_script.sh

. ./some_script.sh

有些脚本退出时带有错误代码;其他人只是没有明确地完成exit。可以使用子 shell 从命令行调用应用程序脚本本身,例如:

echo "Parameter" |
./feed_parameter_to_script.sh script_using_tmps.sh

理想情况下,应用程序脚本可以在其自己的代码中调用从子 shell 创建临时文件,但这不是强制性的。

我找到的唯一解决方案是让每个脚本自行执行 tmp 删除,这不符合我的要求中的第 3 项。

像我这样的需求的最佳实践是什么?

答案1

如果您不能让脚本清理自己的混乱,这将是理想的最佳实践,那么我可以立即想到的下一个最佳选择是使用存在于已知位置且包含数据的信号量文件每个脚本都需要找到它的文件。例如:

#!/bin/bash
if [[ -r $HOME/run/scratchfiles ]]; then
    scratchfiles="$(cat $HOME/run/scratchfiles)"
else
    scratchfiles=$(mktemp -d) && printf "%s" "$scratchfiles" > $HOME/run/scratchfiles
fi
[...]

任何负责清理不再需要的文件的脚本都可以简单地[[ -d "$scratchfiles" ]] && rm -fr "$scratchfiles"给出上述内容。

答案2

如果您使用的是 Bash,我withtemp不久前编写了一个实用程序函数来处理临时文件。该函数在子 shell 中运行并执行命令,将临时文件路径附加到参数列表,或者,如果指定了替换字符串(例如 ){},则替换{}为所有临时文件和{n}第 n 个临时文件。定义了以下选项:

  • -s:删除文件时使用shred而不是。rm
  • -c <command>:评估每个command,将stdout重定向到下一个临时文件,额外的命令将被重定向到最后一个文件。
  • -n <number>:要创建的临时文件数。
  • -r <replacement>:使用替换字符串。
bash-5.1$ type withtemp
withtemp is a function
withtemp () 
{ 
    ( local -a RM=(rm -f);
    local -a COMMANDS=();
    local REPSTR='';
    local REPSTRN='';
    local -i NUMFILES=1;
    local TMPFILE;
    local -a TMPFILES=();
    local -a TMPFDS=();
    local -i i;
    unset OPTIND;
    while getopts sc:n:r: OPTION; do
        case "$OPTION" in 
            s)
                RM=(shred -zu)
            ;;
            c)
                COMMANDS+=("$OPTARG")
            ;;
            n)
                NUMFILES=$OPTARG
            ;;
            r)
                REPSTR=$OPTARG
            ;;
        esac;
    done;
    [ $NUMFILES -le 0 ] && NUMFILES=1;
    for ((i = 0; i < $NUMFILES; ++i))
    do
        TMPFILE=$(mktemp);
        exec {TMPFDS[$i]}> $TMPFILE;
        TMPFILES+=($TMPFILE);
    done;
    trap '"${RM[@]}" "${TMPFILES[@]}"' EXIT;
    i=0;
    for c in "${COMMANDS[@]}";
    do
        eval "$c" 1>&${TMPFDS[$i]};
        [ $((i + 1)) -lt ${#TMPFDS[@]} ] && (( ++i ));
    done;
    for i in ${TMPFDS[@]};
    do
        exec {i}>&-;
    done;
    shift $OPTIND
    if [ -n "$REPSTR" ]; then
        set -- "${@//$REPSTR/${TMPFILES[@]}}";
        for i in "${!TMPFILES[@]}";
        do
            REPSTRN=${REPSTR:0:-1}$((i + 1))${REPSTR: -1};
            set -- "${@//$REPSTRN/${TMPFILES[$i]}}";
        done;
    else
        set -- "$@" "${TMPFILES[@]}";
    fi;
    eval "$@" )
}
bash-5.1$ withtemp -n2 -c 'echo executing first command >&2; echo temp file one contents' \
                   -c 'echo executing second command >&2; echo temp file two contents' \
                   head -v
executing first command
executing second command
==> /tmp/tmp.47hSk9mVe4 <==
temp file one contents

==> /tmp/tmp.tBWnCc2ri1 <==
temp file two contents
bash-5.1$ withtemp -s -n2 -r{} -c 'tac /etc/passwd' -c 'tac /etc/group' \
                   'grep ^root: {2} {1}'
/tmp/tmp.OwMdFfuEMw:root:x:0:root
/tmp/tmp.eTXi6Ti17n:root:x:0:0::/root:/bin/bash

相关内容