12

I use the source command in my bash script in order to read/print the variables values

more linuxmachines_mount_point.txt

export linuxmachine01="sdb sdc sdf sdd sde sdg"
export linuxmachine02="sde sdd sdb sdf sdc"
export linuxmachine03="sdb sdd sdc sde sdf"
export linuxmachine06="sdb sde sdf sdd"

source  linuxmachines_mount_point.txt

echo $linuxmachine01
sdb sdc sdf sdd sde sdg

What is the opposite of source in order to unset the variables?

Expected results

echo $linuxmachine01

< no output >
7
  • 15
    It is not source that is setting the variables in your environment, but the export statements in the file which you source. So the opposite of source could be source, if you source a different file which unsets all the same variables. Dec 30, 2017 at 19:36
  • 2
    yael, don't use exports, just use name="val". Export is for script-to-binaries variables (the environment).
    – ctrl-d
    Dec 30, 2017 at 19:51
  • 1
    Related: unix.stackexchange.com/q/382618/117549
    – Jeff Schaller
    Dec 30, 2017 at 20:00
  • 2
    This is impossible. The concept you are looking for is called Reversible Computing. Programming languages need to be specifically designed with some severe restrictions in order to be reversible. Bash is not one of those programming languages. Dec 31, 2017 at 0:35
  • 1
    @yael, that's untrue (and you obviously never ran the test you're telling ctrl-d to do); you absolutely do not need the exports. All export does is copy the values into the environment -- but they're present as shell variables whether or not they're defined as environment variables as well. Moreover, defining unnecessary environment variables shortens your maximum command line length, as they're stored in the same (limited!) per-process space. Dec 31, 2017 at 18:57

7 Answers 7

24

Using a subshell (Recommended)

Run the source command in a subshell:

(
source linuxmachines_mount_point.txt
cmd1 $linuxmachine02
other_commands_using_variables
etc
)
echo $linuxmachine01  # Will return nothing

Subshells are defined by parens: (...). Any shell variables set within the subshell are forgotten when the subshell ends.

Using unset

This unsets any variable exported by linuxmachines_mount_point.txt:

unset $(awk -F'[ =]+' '/^export/{print $2}' linuxmachines_mount_point.txt)
  • -F'[ =]+' tells awk to use any combination of spaces and equal signs as the field separator.

  • /^export/{print $2}

    This tells awk to select lines that begin with export and then print the second field.

  • unset $(...)

    This runs the command inside $(...), captures its stdout, and unsets any variables named by its output.

3
  • why not to use this - for i in ` awk '{print$2}' file | awk -F"=" '{print$1}' `; do unset $i; done ? ( more simple for unset )
    – yael
    Dec 30, 2017 at 20:37
  • 2
    @yael That uses an extra process and a loop when neither are needed. Opinions may vary but I don't see that as simpler. Also and potentially dangerous, it assumes that every line in file is an export statement.
    – John1024
    Dec 30, 2017 at 20:59
  • 8
    @yael Also, unsetting variables and undoing other code side-effects is exactly what subshells are for. They do it simply and reliably. Unless there is some special reason otherwise, subshells are what you should be using.
    – John1024
    Dec 30, 2017 at 21:06
4

You cannot unsource the script.

What you can do is to store all exported variables in a temporary file, compare it with variables after the script is sourced, and then removed overflow with unset, e.g.:

export > temp_file
source myscript

#... do some stuff

unset "$(comm -3 <(sort temp_file) <(export | sort) | awk -F'[ =]' '{print $3}' | tr '\n' ' ')"
4
  • why not to use this - for i in ` awk '{print$2}' file | awk -F"=" '{print$1}' `; do unset $i; done ? ( more simple for unset )
    – yael
    Dec 30, 2017 at 20:37
  • @yael it will work only if script contains only exports.
    – jimmij
    Dec 30, 2017 at 20:49
  • what you mean "export s" , my file include linuxmachine01="sdb sdc sdf sdd sde sdg" and so on , and when I use the awk loop its unset the variable without problems
    – yael
    Dec 30, 2017 at 20:56
  • @yael I don't know how exactly your file looks like, so I gave some general answer, for example what if the script contains a comment at the top saying "# Here are some variable to export with mount points.".
    – jimmij
    Dec 30, 2017 at 21:04
2

You can use unset command to "forget" variables.

5
  • file could be with diff machines , so how to unset the second variables in the file?
    – yael
    Dec 30, 2017 at 19:41
  • unset variablename will forget it (you can find it explained in bash/sh/ksh/zsh man page) demo of it : pastebin.com/MGQyTZEA
    – francois P
    Dec 30, 2017 at 19:44
  • yes but because variables could be diff , bash script need to do it by read the variable from the file and unset them , it cant be static because variable are unknown
    – yael
    Dec 30, 2017 at 19:47
  • then grep yours export lines (cut) to list variables from the file to be unset export linuxmachine06="sdb sde sdf sdd" then you get linuxmachine06 as a name to unset example : pastebin.com/M9eCtLc7 as you see the unset command here doen't know the variable name it is known only at end
    – francois P
    Dec 30, 2017 at 19:53
  • ok I will use this syntax in order to unset - for i in ` awk '{print$2}' file | awk -F"=" '{print$1}' `; do unset $i; done
    – yael
    Dec 30, 2017 at 20:09
2

The simplest solution to have the output you expect (nothing) is to re-declare the variable as empty:

$ export linuxmachine01="sdb sdc sdf sdd sde sdg"
$ echo "$linuxmachine01"
sdb sdc sdf sdd sde sdg

$ linuxmachine01=""
$ echo "$linuxmachine01"
$

Of course the variable is still defined (and exported), empty but defined:

$ declare -p linuxmachine01
declare -x linuxmachine01=""

To correctly remove the variable from both the environment and the running shell you should use unset (the recommended way):

$ unset linuxmachine01
$ declare -p linuxmachine01
bash: declare: linuxmachine01: not found
$ echo "$linuxmachine01"
$
1

The simplest way to do this is to modify your script so it also defines a command to undo the effect of the script:

export linuxmachine01="sdb sdc sdf sdd sde sdg"
export linuxmachine02="sde sdd sdb sdf sdc"
export linuxmachine03="sdb sdd sdc sde sdf"
export linuxmachine06="sdb sde sdf sdd"
alias linuxmachines_mount_point='for v in linuxmachine01 linuxmachine02 linuxmachine03 linuxmachine04; do unset $v; done; unalias linuxmachines_mount_point'
4
  • Why an alias rather than a function? Note that aliases are disabled in noninteractive shells by default, so out-of-the-box, this won't work in a script. Dec 31, 2017 at 19:00
  • ...actually, this would be even easier if we defined just one variable, and thus had only one to unset: declare -A linuxmachines=( [01]="sdb sdc sdf" [02]="sde sdd sdb" [03]="whatever" ), then it's just unset linuxmachines to revert. Dec 31, 2017 at 19:01
  • @CharlesDuffy: both alias and function are fine for this purpose. The reason I don't suggest putting everything into one variable is that defining an undo command is much more generic. You can undefine not just variables but also functions, aliases, system settings, terminal settings, etc. It provides a better abstraction as you don't have to care about the exact content of the undo command.
    – Lie Ryan
    Dec 31, 2017 at 19:25
  • I believe my point above is that aliases are not fine for the purpose, if "the purpose" includes noninteractive use (ie. invocation from scripts). Dec 31, 2017 at 19:42
1

You can write your linuxmachines_mount_point.txt like this

test "$linuxmachine01" && unset -v linuxmachine01 || linuxmachine01="sdb sdc sdf sdd sde sdg"

When you need your variables

source linuxmachines_mount_point.txt

When you want to remove the variables

source linuxmachines_mount_point.txt
0

If you're using the source command to activate a VirtualEnvironment, you can exit it using the command deactivate.

You must log in to answer this question.

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