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?
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?
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>&-
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.
trap "rm -f $temp_file" 0 2 3 15
right after creating the file so that when the script exits or is stopped with ctrl-C
the file is still removed.
– wurtel
Jan 30 '15 at 07:27
kill -9 $somepid
. That particular kill signal is insta-death with nothing else happening.
– dragon788
Jul 21 '17 at 22:08
bash -c 'echo $$; trap "echo foo" 0; sleep 5'
– Hauke Laging
Aug 05 '17 at 06:22
trap "rm -f $temp_file" 0 2 3 15
are the signals upon which to run the first argument. 0: exit shell, 2: Interrupt, 3: Quit, 15: Terminate.
– ijoseph
May 23 '20 at 18:41
trap "rm -f $temp_file" EXIT INT QUIT TERM
– sengi
Oct 06 '22 at 09:39
mktemp
fails without argument and prints something like Usage: mktemp [-d] [-q] [-u] template
instead
– Tino
May 07 '23 at 14:24
Some shells have the feature built-in.
zsh
's =(...)
form of process substitution uses a temporary file. For instance =(echo test)
expands to the path of a temporary file that contains test\n
.
$ {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.
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 test\n
.
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).
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).
=(...)
.
– Stéphane Chazelas
Jan 30 '15 at 21:01
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 "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"
tmpfile
still be removed before script exit, but not when script received other signals.
– cuonglm
Jan 30 '15 at 08:00
INT
, tmpfile
will be removed immediately when you press Ctrl+C
, not when script exit.
– cuonglm
Jan 30 '15 at 08:10
mktemp
originated in HP/UX with a different syntax. Todd C. Miller created a different one for OpenBSD in the mid-90s (copied by FreeBSD and NetBSD) and later made it also available as a standalone utility (www.mktemp.org). That's the one that was typically used on Linux until a (mostly compatible) mktemp
utility was added to the GNU coreutils in 2007. Just to say one cannot really say mktemp
is a GNU utility.
– Stéphane Chazelas
Jan 30 '15 at 22:01
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
Is there any way to have the file automatically deleted if the program crashes, but making it accessible multiple times?
– smihael Aug 24 '17 at 22:09exec {FD_R2}<"$tmpfile"
before the call to rm "$tmpfile"
Then, to read it first time, use cat <&$FD_R
. And, to read it the second time, use cat <&$FD_R2
. To generalize, if you need to read it n times, you can create n read file descriptors.
– Gino
Apr 21 '21 at 04:13
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).
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.
/proc
- except for systems that don't have it. – Dennis Williamson Jan 30 '15 at 20:33exec 3> "$tmpfile"
do? Isn't that only useful if the tmpfile is a stand-alone script? – Alexej Magura Nov 29 '16 at 18:36cat <3
or something similar. Remember stdin and stdout just happen to live on 1 and 2, you can move them around to other descriptors easily. – dragon788 Jul 21 '17 at 22:02mkdir /tmp/del ; cd /tmp/del ; rmdir /tmp/del; ls -l /proc/self/cwd
but it seems less useful than with files because you cannot store anything in such a directory (except for "deleted files"). – Hauke Laging Oct 16 '18 at 20:21rename
op to atomically commit is cool. But if that temporary directory is guaranteed to be deleted when the process is, then I don't have to worry about external garbage collection. – CMCDragonkai Oct 18 '18 at 01:22cat <&3
will giveBad file descriptor
. I'd appreciate it if you either fix it or remove it; misinformation doesn't much help. – erik258 Feb 21 '19 at 05:24