21

Sometimes I use, $PROJECT_HOME/* to delete all files in the project. When the environment variable, PROJECT_HOME is not set (because I did su and the new user doesn't have this environment variable set), it starts deleting all files from the root folder. This is apocalyptic.

How can I configure bash to throw error, when I use an undefined environment variable in the shell?

  • 5
    set -u will do what you want. – cuonglm Jun 8 '15 at 6:27
  • can you make it as the answer? – Madhavan Jun 8 '15 at 6:27
  • 2
    Of course you wouldn't initialize your vars to empty strings. [ -z "$VAR" ] works with an uninitialized VAR too. The initialization was just to show the undesirable behavior—My point is, if your vars ever do become initialized to empty strings, in whatever way, and you run rm -r "$PROJECT_HOME"/* mistakenly relying on set -u, you will get the "apocalyptic" behavior. IHMO, it's better to be safe than sorry when it comes to protecting the entire contents of your computer. set -u is not safe. – PSkocik Jun 8 '15 at 8:00
  • 3
    "It is very long"? You should not be looking for a convenient way to manually perform dangerous operations. Instead, you should create a function, alias, or script to do what you want; in this case, making in an alias from @PSkocik's suggested command will be both safe and convenient. – Kyle Strand Jun 8 '15 at 17:32
  • 1
    What if the user sets PROJECT_HOME=/etc? Just checking for an empty value is not enough to prevent cataclysm. You shouldn't use variables from untrusted users when running as root. – Barmar Jun 10 '15 at 19:40
28

In POSIX shell, you can use set -u:

#!/bin/sh

set -u
: "${UNSET_VAR}"

or using Parameter Expansion:

: "${UNSET_VAR?Unset variable}"

In your case, you should use :? instead of ? to also fail on set but empty variables:

rm -rf -- "${PROJECT_HOME:?PROJECT_HOME empty or unset}"/*
18
[ -z "$PROJECT_HOME" ] || rm -r "$PROJECT_HOME"/*

This will also catch the case where PROJECT_HOME is set but doesn't contain anything.

Example:

1) This will delete pretty much everything you can delete on your system (barring dotfiles inside / (there aren't usually any)):

set -u
PROJECT_HOME=
rm -r "$PROJECT_HOME"/*

2) This won't do anything:

PROJECT_HOME=
[ -z "$PROJECT_HOME" ] || rm -r "$PROJECT_HOME"/* 

Completely removing your project home and recreating it might be another option (if you want to get rid of dotfiles too):

#no apocalyptic threats in this scenario
rm -r "$PROJECT_HOME"
mkdir "$_" 
  • 1
    -z is ok, but since $PROJECT_HOME is supposed to be a directory, maybe -d would be better. [[ -d $PROJECT_HOME ]] && rm -r "$PROJECT_HOME". – kojiro Jun 8 '15 at 11:41
  • 2
    If PROJECT_HOME is set to a nondirectory, that's an noncatastrophic error, possibly due to a typo. The -d check would hide that error. I think it's better if it goes on to rm and rm complains about it out loud. – PSkocik Jun 8 '15 at 11:50
  • 2
    If PROJECT_HOME is set, but the name is not a directory, then [[ -d $PROJECT_HOME ]] && rm -r "$PROJECT_HOME" will do nothing, silently. But [[ -z $PROJECT_HOME ]] || rm -r "$PROJECT_HOME" will silently delete "$PROJECT_HOME", even if it's a file that is not a directory. Getting an error message is never a problem: if [[ -d $PROJECT_HOME ]]; then rm -r "$PROJECT_HOME"; else printf '%s is not a directory\n' "$PROJECT_HOME" >&2; fi – kojiro Jun 8 '15 at 13:30
  • 1
    Now "accidentally" set PROJECT_HOME="/." ... – Hagen von Eitzen Jun 8 '15 at 15:13
  • 1
    @HagenvonEitzen If PROJECT_HOME is set to root, the procedure that empties PROJECT_HOME will empty root. That's the expected and perfectly reasonable behavior. And you don't even need that final dot. – PSkocik Jun 8 '15 at 15:28
0

Another way to do this:

rm -r "${somevar:-/tmp/or_this_if_somevar_is_empty}"/*

There are many variable substitutions, the one above is when "somevar" is empty (and in that case it attempts to delete /tmp/or_this_if_somevar_is_empty/* )

  • 1
    Or: rm -fr ${ENV_VAR:?suitably caustic message here}/*, but it might still be worth checking that the value doesn't map to the root directory, noting that there are many ways to subvert simple tests: //, /.., /usr/who/../.., … – Jonathan Leffler Jun 8 '15 at 13:45
  • Yes. If you're going to use a parameter expansion, you may as well use the one that actually throws an error – Digital Trauma Jun 8 '15 at 22:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.