How create a temporary file in shell script?

While running a script, I want to create a temporary file in /tmp directory.

After execution of that script, that will be cleaned by that script.

How to do that in shell script?

Asked By: Bhuvanesh

||
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
: ...
rm "$tmpfile"

You can make sure that a file is deleted when the scripts exits (including kills and crashes) by opening a file descriptor to the file and deleting it. The file keeps available (for the script; not really for other processes but /proc/$PID/fd/$FD is a work-around) as long as the file descriptor is open. When it gets closed (which the kernel does automatically when the process exits) the filesystem deletes the file.

# create temporary file
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)

# create file descriptor 3 for writing to a temporary file so that
# echo ... >&3 writes to that file
exec 3>"$tmpfile"

# create file descriptor 4 for reading from the same file so that
# the file seek positions for reading and writing can be different
exec 4<"$tmpfile"

# delete temp file; the directory entry is deleted at once; the reference counter
# of the inode is decremented only after the file descriptor has been closed.
# The file content blocks are deallocated (this is the real deletion) when the
# reference counter drops to zero.
rm "$tmpfile"

# your script continues
: ...

# example of writing to file descriptor
echo foo >&3

# your script continues
: ...

# reading from that file descriptor
head -n 1 <&4

# close the file descriptor (done automatically when script exits)
# see section 2.7.6 of the POSIX definition of the Shell Command Language
exec 3>&-
Answered By: Hauke Laging

Use mktemp to create a temporary file. The utility returns the full path of the created file.

temp_file=$(mktemp)

Or, to create a temporary directory:

temp_dir=$(mktemp -d)

At the end of the script, you may want to delete the temporary file or directory:

rm "${temp_file}"
rm -r "${temp_dir}"

Note: mktemp creates a file in the /tmp directory or in the directory given with the --tmpdir argument. See the utility’s manual for other options and how to modify its behaviour in other ways.

Answered By: chaos

If you’re on system which has mktemp, you should use it as other answers.

With POSIX toolchest:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%dn", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"
Answered By: cuonglm

Some shells have the feature built-in.

zsh

zsh‘s =(...) form of process substitution uses a temporary file. For instance =(echo test) expands to the path of a temporary file that contains testn.

$ {cat $file; ls -l /dev/fd/3; echo test2 >&3; cat $file} 3<> ${file::==(echo test)}
test
lrwx------ 1 stephane stephane 64 Jan 30 11:19 /dev/fd/3 -> /tmp/zshMLbER0
test2

That file is automatically removed, once the command has finished.

bash/zsh on Linux.

Here-documents or here-strings in bash versions prior to 5.1 and zsh are implemented as deleted temporary files (as was the case in the Bourne shell which introduced here-documents in the late 70s).

So if you do:

exec 3<<< test

The file descriptor 3 is connected to a deleted temporary file that contains testn.

You can get its content with:

cat <&3

If on Linux, you can also read or write to that file via /dev/fd/3, though with bash version 5.0, you’d first to need to restore write permissions to it (which bash explicitly removes in that version):

$ exec 3<<< test
$ cat <&3
test
$ chmod u+w /dev/fd/3 # only needed in bash 5.0
$ echo foo > /dev/fd/3
$ cat /dev/fd/3
foo

(some other shells use pipes, or may use /dev/null if the here doc is empty).

POSIX

There is no mktemp POSIX utility. POSIX however specifies a mkstemp(template) C API, and the m4 standard utility exposes that API with the mkstemp() m4 function by the same name.

mkstemp() gives you a file name with a random part that was guaranteed not to exist at the time the function was called. It does create the file with permissions 0600 in a race-free way.

So, you could do:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

Note however that you need to handle the clean-up upon exit, though if you only need to write and read the file a fixed number of times, you could open it and delete it just after creating like for the here-doc/here-string approach above:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

# open once for writing, twice for reading:
exec 3> "$tempfile" 4< "$tempfile" 5< "$tempfile"

rm -f -- "$tmpfile"

cmd >&3   # store something in the temp file
exec 3>&- # fd no longer needed

# read the content twice:
cat <&4
cat <&5

You could open the file for reading once, and rewind in between two reads, however there’s no POSIX utility that can do that rewinding (lseek()), so you can’t do it portably in a POSIX script (zsh (sysseek builtin) and ksh93 (<#((...)) operator) can do it though).

Answered By: Stéphane Chazelas

Here is a little bit improved answer in the line of Hauke Laging’s:

#!/bin/bash

tmpfile=$(mktemp)  # Create a temporal file in the default temporal folder of the system

# Lets do some magic for the tmpfile to be removed when this script ends, even if it crashes
exec {FD_W}>"$tmpfile"  # Create file descriptor for writing, using first number available
exec {FD_R}<"$tmpfile"  # Create file descriptor for reading, using first number available
rm "$tmpfile"  # Delete the file, but file descriptors keep available for this script

# Now it is possible to work with the temporal file
echo foo >&$FD_W
echo bar >&$FD_W  # Note that file descriptor always concatenates, not overwrites

cat <&$FD_R
Answered By: SwanS

My workflow typically with temp files is because of some bash script I’m testing. I want to tee it up so I can see that it’s working and save the output for the
next iteration of my process. I’ve created a file called tmp

#!/bin/bash
echo $(mktemp /tmp/$(date +"%Y-%m-%d_%T_XXXXXX"))

so that I can use it like

$ some_command --with --lots --of --stuff | tee $(tmp)

The reason I like the datetime formatted before the random values is it allows me to find the tmp file that I just made easily, and I don’t have to think about what to name it next time (and focus on just getting my dang script to work).

Answered By: Frank Bryce

If you want your solution to work with a pure POSIX shell, it’s a little tricky, because POSIX doesn’t have a mktemp. See Why is there no mktemp command in POSIX? for more information.

However, here is a work-around solution for POSIX shell that doesn’t require m4 and implements at least basic mktemp as well as the -d/--directory option to create temporary folders as well:

#!/bin/sh
mktemp() (
    create=f
    for option; do
        case $option in
            -d|--directory) create=d;;
            *) printf %s\n "$0: unknown option '$option'" >&2; exit 1;;
        esac
    done
    set -o noclobber
    exec 2>/dev/null
    while :; do
        tmpname="$TMPDIR/tmp.$(</dev/urandom tr -dc "[:alnum:]" | dd bs=1 count=10 2>/dev/null)"
        case $create in
            f) umask 0177; printf "" >"$tmpname";;
            d) umask 0077; mkdir "$tmpname";;
        esac && break
    done
    printf %s\n "$tmpname"
)

Of course, it would be possible to further extend this function with option parsing via getopts, for example, to add support for templates etc.

Answered By: finefoot
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.