I was writing some scripts and wrote something like

[ -d $ARTIFCATS ] && rm -rf $ARTIFACTS/*

What happened is that out of stupidity I executed the second line without executing the first. It turned out that [ -d "" ] returns true and the expression became

rm -rf /*

Luckily it was only a test machine and I wasn't a sudo, but though I lost some data

My question is, why [ -d "" ] return true?? the documentation clearly states it checks whether a path exists and is a folder

I solved the problem by using

which seems to work


  • 5
    Or maybe you executed both lines. In the code example above, you never set ARTIFCATS.
    – Buhb
    Oct 8, 2013 at 13:00
  • 2
    I would just write that as rm -rf $ARTIFACTS without the /*. This would also delete the $ARTIFACTS directory, which is fine, because if i want to be sure that it exists before putting something in it, i will execute mkdir -p $ARTIFACTS anyway. It will also delete hidden files inside $ARTIFACTS, which is also fine, because i wouldn't write rm -rf $ARTIFACTS/* if $ARTIFACTS contained anything i wanted to save. Oct 8, 2013 at 15:54
  • @ChristofferHammarström very true Oct 9, 2013 at 9:39

4 Answers 4


1. These two tests return true:

# [ -d ] && echo true || echo false
# [ -d $SOME_UNSET_VAR ] && echo true || echo false

according to POSIX (as explained by @Tim).

2. But this returns false (not true as stated in the question)

# [ -d "" ] && echo true || echo false

because test is called with two arguments (although the second one is an empty string).

3. That's why it is good practice to use [[ … ]] instead of test ([ … ]), which most (all?) current shells provide. This construct checks if you supply enough arguments (otherwise throws an error and aborts)

# [[ -d ]] && echo true || echo false
bash: unexpected argument `]]' to conditional unary operator
bash: syntax error near `]]'

or simply behaves like one would expect:

# [[ -d $SOME_UNSET_VAR ]] && echo true || echo false

4. And, as pointed out by @Gilles, even more important is to double quote substitutions. So -d "$SOME_UNSET_VAR" expands to -d "" and returns false even with test (equal to case 2). Hence this is also compatible with the Bourne shell sh:

# [ -d "$SOME_UNSET_VAR" ] && echo true || echo false

tested with bash 3.00.16(1) and 4.1.5(1)


This question has already been answered on StackOverflow. It says that according to the POSIX standard, test should always return successful if it is called with exactly one non-empty argument (and no other arguments).

This should also be the case with test -e (and in fact it is on my system), so be careful.

Instead use:

[ -d "$ARTIFACTS" ]

test will then be called with two arguments even if the variable is empty and return false in this case.

  • "exactly one nonempty argument." - this is a misleading summary - the page you linked mentions being called with exactly one argument and that argument being non-empty; nothing about the case of a nonempty argument followed by an empty one. The correct solution should be to surround the variable name in quotes.
    – Random832
    Oct 8, 2013 at 13:28
  • @Random832 Thank you! I implemented both of your suggested changes.
    – Tim
    Oct 8, 2013 at 14:35
  • [ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ] is no better: it only caters for the particular case when $ARTIFACTS is empty, but fails when $ARTIFACTS may contain whitespace or \[?*. [ -d "$ARTIFACTS" ] is the correct way to do it (or [[ -d $ARTIFACTS ]] in shells that have [[ … ]]). Oct 8, 2013 at 17:16
  • @Gilles You are right, I removed it.Thank you!
    – Tim
    Oct 8, 2013 at 20:22

I solved the problem by using [ -e $ARTIFACTS ] which seems to work

You are wrong. It works because $ARTIFACTS is now set to something.

When a variable isn't set, then saying

[ -d $SOMEVAR ]


[ -e $SOMEVAR ]

would both evaluate to true because it implies saying

[ -d ]


[ -e ]

respectively. (Saying [ foobar ] would always evaluate to true.)


set -u

comes in handy in such situations. help set would tell you:

  -u  Treat unset variables as an error when substituting.
  • [ -e "" ] && echo "PRINT SOMETHING" does not print anything, so how can this be wrong? Oct 8, 2013 at 11:23
  • 2
    ahh crap [ -e $ARTIFACTS ] with empty artifacts yields [ -e ] not [ -e "" ], got it Oct 8, 2013 at 11:30

See that you set the variable ARTIFACTS and you were checking for ARTIFCATS. Probably mistyping?

Anyway, -d as well as -e would produce same results on unset variables.

Hence use double quotes and it will help you.

[ -d "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS/"*

NOTE: If your "/SOME/PATH" has any folder with space, the script you mentioned will break with "binary operator expected" error.


ARTIFACTS="/home backup/"

1) [ -d $ARTIFACTS ] && rm -rf $ARTIFACTS/*
bash: [: /home: binary operator expected

2) [ -d "$ARTIFACTS" ] && rm -rf "$ARTIFACTS"/*

will do fine. Don't forget to put quotes in the rm invocation as well (rm -rf $ARTIFACTS would cheerfully remove /home then complain about backup/* not existing).

Also, including -L check will make sure that it is a directory and not just a symbolic link to a directory.

So, basically,

[ -d "$ARTIFACTS" && ! -L "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS"/*

You must log in to answer this question.

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