0

I need to create a reusable ("utility") script handling entire set of operations with temporary files for any of my other ("application") scripts:

  1. creation
  2. tracking of temporaries created
  3. trapping exit (regular completion and interruption by user) and removing everything created

Each of application scripts need to "include" utility script on their own, not rely that some "main" script will call it once for all subscripts it uses.

The key requirement is that application scripts using the utility script can call each other, each of them in turn also need to use temporary files. Call between scripts are both

./some_script.sh

and

. ./some_script.sh

Some scripts exit with error code; others just finish without explicit exit. The application scripts themselves can be called from command line using subshells, like:

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

Ideally application scripts could invoke creating temporaries from subshells within its own code, but that's not mandatory.

The only solution I found is for each script to perform its tmp removal on its own, which does not fit item 3. in my requirements.

What is best practice for requirements like mine?

1
  • You know mktemp can be used to create directories too? Then your trap becomes not much more than [ -n "$mytempdir" ] && [ -d "$mytempdir" ] && rm -rf "$mytempdir" with no need to track temporaries created inside it. That's one line for the mktemp, one or two lines for the trap: not a lot of overhead really Jul 20, 2022 at 18:08

2 Answers 2

0

If you can't have the scripts clean up their own messes, as would be the ideal best practice, then the next best option I can think off offhand would be to use semaphore files that would exist in a known location which would have the data each script would need to find its files. e. g. :

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

Any of the scripts which is responsible for cleaning up no-longer-needed files could then simply [[ -d "$scratchfiles" ]] && rm -fr "$scratchfiles" given the foregoing.

0

If you're using Bash, I wrote an utility function withtemp to deal with temporary files some time ago. The function runs in a subshell and executes a command, appending the temporary file paths to the argument list, or, if a replacement string, say {}, is specified, replacing {} with all temporary files and {n} with the nth temporary file. The following options are defined:

  • -s: Use shred instead of rm when deleting files.
  • -c <command>: Evaluate each command, redirecting stdout to the next temporary file, extra commands will be redirected to the last file.
  • -n <number>: Number of temporary files to create.
  • -r <replacement>: Use replacement string.
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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .