Is it good to delete the variables used in a shell-script at the end of a script.
rm -rf $abc
rm -rf $def
or
unset $abc
unset $def
and so on.
Is this actually a good practice?
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It only takes a minute to sign up.
Sign up to join this communityThis is a very bad practice.
rm
deletes files. It is nothing to do with variables. In any case, the variables themselves will be disposed of when the script ends and the operating system reclaims the shell's memory.
In the simple case, a file with the same name as the value of one of the variables will be deleted by this construction:
abc=filename
rm -f $abc # Deletes "filename" in the current directory
It gets worse. If abc
and def
contain the names of files as individual words separated by a space (or any other character of IFS
), you will delete those files, and wildcards like *
will be expanded too if they appear in any of the words.
abc='hello world'
rm -f $abc # Deletes file "hello" and "world" (leaves "hello world" alone)
abc='5 * 3'
rm -f $abc # Deletes all files, because * is expanded (!)
def='-r /'
rm -f $def # Really deletes *all* files this user can access
Shell parameter expansion with $var
is subject to word splitting, where every character of the IFS
variable divides the variable into different arguments. Each word is then subject to filename expansion, which uses *
, ?
, and [abc...]
patterns to create filenames. This could get very bad, depending on what your variables have in them. Do not do this.
There is no need to blank or unset variables at the end of a shell script in any way.
unset
. But it is not neccessary to unset them as they are not transferred to the calling shell, if you simply execute the script (you'd need to call . yourscript.sh
if you would like to have the variables of the script in your currecnt shell environment)
Apr 21, 2017 at 8:03
-r /
) on several current rm
implementations (see unix.stackexchange.com/a/19548/2594 )
rm
part, but not with the "_ the variables themselves will be disposed of when the script ends_" part; the after the script exits, the variables will still be set in the shell where the script ran.
Mar 24 at 12:52
.
or source
, then all the variables (exported or otherwise) will be available from the shell you ran your script. Otherwise (e.g., by calling it via an interpreter like bash
) it will run in a subshell, and then it doesn't matter if you exported your variables or not as they will be discarded once the scripts exits (and thus the subshell ends).
Mar 26 at 20:31
I wonder if you actually mean shell variables, or temporary files.
If what you're doing is this (a temporary file):
tmp=$(mktemp)
something > "$tmp"
something else < "$tmp"
rm "$tmp"
Then sure, go ahead and remove the temp file after you're done. Though if the script crashes in the middle, the file will be left laying there, but that's not exactly uncommon either. You could use trap 'rm -f -- "$tmp"' EXIT
to remove the file when the shell exits, if you care.
You should probably use rm -f -- "$tmp", as
-fwould help in avoiding error messages if the file was e.g. removed earlier for some reason. No reason to use
-r` when removing a single file, though.
(Also, remember to quote the variable, even though mktemp
by default makes "nice" filenames.)
However, if you're doing this (a variable):
read var
do something with "$var"
rm "$var"
Then you don't want to do that: you have no file to remove, just something entered by the user. Shell variables cease to exist when the shell exits, so no need to unset them.
Though if your script is meant to be sourced from another shell (or it's in .bashrc
or similar), the shell doesn't exit when your script ends. In that case it might be useful to unset
any temporary variables you used at the end, to not leave them set for the rest of the shell's lifetime.
# in .bashrc
__hour=$(date +%H)
if [ "$__hour" -lt 12 ] ; then echo "Good Morning!" ;
elif [ "$__hour" -gt 18 ] ; then echo "Good Evening!" ;
fi
unset __hour
The variable name might still collide with some variable used outside that script so some care must be taken with naming.
set -- "$(date +'%H')" ${1+"$@"}; <do whatever with $1>; shift;
We never need worry about whether __hour
pre-exists in the environment where we are to source stuff.
local
builtin to keep variables within the scope of the function and its children without polluting the global environment.
Dec 17, 2021 at 18:12
-f
, rm
may prompt the user which we likely don't want here. rm -f -- "$file"
would be more correct and would have also have the benefit of not returning an error if the file was already gone.
Mar 26 at 19:44
bash
, unset
may unset a function, if a variable by that name is not found. In that shell, it's a good habit to use unset -v var
to make sure you unset a variable.
Mar 26 at 19:45
I was just got bugged by an erroneous output from my shell script today. The reason is that, exported environment variables can actually creep-in into your shell script and cause nasty side-effects. Here is how:
#!/bin/sh
#
line="$line $1"
echo $line
When invoked as, myscript.sh smeagol
, an already exported line
variable creeps in. I have to unset line
in my script entry to avoid pollution.
unset
to ensure previously defined vars will not introduce nasty and hard to debug side-effects. It could drive you to the edge...
No.rm
doesn't deal with variables. If you wrote rm $abc
it will delete the file in current directory with the name stored in variable abc
.
Example: A variable abc
stores value 52. and you performed rm $abc
then it will delete file 52 in your current directory (use pwd
command to print current directory and ls
to list files in current directory).
Variables will be disposed automatically (at least in shell script) if you are not running it with .
or source
.
It depends on your use case(s) and intention(s) because a shell script can be run in different ways (see "1. Ways to call shell scripts" section below) which affect whether the variables are retained by the origin shell (the shell the script is called from) or not.
For example, a couple of cases for and against unset
ting variables:
Bad, if the point of the script is to set environment variables3
Then again, this also depends on how the shell script is called (see section 1.).
Bad, if the script is intended to expose shell variables for some purpose.
For example, the script is a "module" in a larger "shell script suite", and other shell scripts depend on the variables set by the called "sub-scripts". (The same caveat applies as in the previous item; see section 1.)
Good, if one wants to protect ignorant users (myself included) AND ->
if the previous points don't apply<-
(many times they do not).
NOTE: ignorant = less knowledgeable
For example, an "unset section", where all the shell variables are enumerated (see example) saves me time and future anguish; I don't write shell scripts every day, and even though I leave excessive amount of notes to myself when I do, muscle memory in my hands doesn't care that I'm running the script in a way that will result in long debugging sessions (once again, see "1. Ways to call shell scripts" section below). Also, if the scripts are expected to be used by others, even good documentation doesn't help, because that is usually ignored (my ignorant self included).
(What else am I missing?)
TL;DR
(ASCII table because the site doesn't support markdown tables; see a better formatted version of this answer in this gist.)
| Shell script calling | Preserves | Relevant |
| method | variables and | section |
| | exports | |
| --------------------------- | --------------- | --------- |
| `<interpreter> <file_name>` | no | 1.1 |
| `.` | yes | 1.2 | (workaround in 1.2.1)
| executable script | no | 1.3 |
| `source` | yes | 1.4 | (workaround in 1.2.1)
<interpreter> <file_name>
For example, if the script is called script.sh
and it is called from its containing directory:
sh script.sh
bash script.sh
zsh script.sh
fish script.sh
This method will start a subshell (Advanced Bash Scripting Guide: Chapter 21. Subshells), that is a new shell in a child process, which means that
shell and environment variables will be discarded after the script has finished running, therefore the shell the script is called from remains unaffected.
If the point of the script is to set environment variables to be used in the current shell, then use:
.
) POSIX standard commandRelevant thread: stackoverflow
: Preserve environments vars after shell script finishes
.
will "execute commands in the current environment" (POSIX standard, dot man page) without starting a subshell.
Shell and environment variables will remain in effect after the script has finished running.
If the same script is run again, it may lead to unexpected behaviour. For example, given the script below (let's call it test.sh
),
#!/usr/bin/env bash
# https://unix.stackexchange.com/questions/129391/passing-named-arguments-to-shell-scripts
while [ $# -gt 0 ]; do
case "$1" in
--option_a|-a)
OPTION_A=$2
;;
--option_b|-b)
OPTION_B=$2
;;
*)
printf "***************************\n"
printf "* Error: Invalid argument.*\n"
printf "***************************\n"
exit 1
esac
shift
shift
done
# Setting default value if script is not called with `--option`
OPTION_A="${OPTION_A:-"default A"}"
OPTION_B="${OPTION_B:-"default B"}"
echo "OPTION_A: ${OPTION_A}"
echo "OPTION_B: ${OPTION_B}"
if it is run via methods described in sections 1.1 and 1.3, all is fine:
$ chmod +x test.sh
$ ./test.sh
OPTION_A: default A
OPTION_B: default B
$ bash test.sh -a 27
OPTION_A: 27
OPTION_B: default B
$ ./test.sh
OPTION_A: default A
OPTION_B: default B
However using .
or source
(see section 1.4 below), it will mess with assumptions, if one hopes use a variable's default value if a command line options is not provided:
# source test.sh === . ./test.sh
$ source test.sh
OPTION_A: default A
OPTION_B: default B
$ . ./test.sh -a 27
OPTION_A: 27
OPTION_B: default B
$ source test.sh
OPTION_A: 27
OPTION_B: default B
.
(and source
) without messing up the current shell?Use parentheses ((
and )
), and the commands between them will be executed in a subshell (which is the same way as with the methods in sections 1.1 and 1.3).
For example, ( source test.sh )
.
Relevant docs and threads:
unix&linux
: Can a script be run as a subshell?unix&linux
: How are parentheses interpreted at the command line?See description in "1.1 <interpreter> <file_name>
" above.
Relevant thread: unix&linux
: Why does Bash's source
not need the execution bit?
For example, if the script is called script.sh
and it is called from its containing directory:
$ chmod +x script.sh
$ ./script.sh
source
commandA synonym for the dot (.
) command (see " 1.2 The dot (.
) POSIX standard command" section above).
As far as I can tell, the source
synonym exists in bash, fish, and zsh, but probably in other shells as well, but it is better to use .
for portability.
Relevant thread: unix&linux
: Why was the dot (.) used as an alias for source & why don't other commands have shortcuts too?
[1]: Reiterating the question, because the answers here focus on rm
and that it doesn't unset variables. To sum up:
rm
deletes files
unset
The $
, which is used for parameter expansion (GNU Bash docs, 3.5.3 Shell Parameter Expansion) in this case2, is not needed before variable_name
; otherwise it will try to unset whatever variable_name
refers to:
$ abc=27
$ def=abc
$ echo "abc=${abc}; def=${def}"
abc=27; def=abc
# v
$ unset $def
# ^
$ echo "abc=${abc}; def=${def}"
abc=; def=abc
# V
$ unset def
# ^
$ echo "abc=${abc}; def=${def}"
abc=; def=
It can be a bit more complicated though: unix&linux
: What does unset
do?.
[2]: See also stackoverflow
: What are the special dollar sign shell variables?
[3]: Good threads in this topic:
abc='5 * 3'
,def='-r /'
).