-2

I would like to have a directory mydir with empty content, but I don't know in advance if it already exists, or when it does, if it is empty. So my way is

mkdir -p mydir                                                                                                           
rm -f mydir/*

Is there any flaw? What is your better solution?

Tim
  • 98,580
  • 191
  • 570
  • 977

2 Answers2

3

The flaw is that you're using an automated forced removal of a directory—and of course to remove its contents you'd need an automated forced recursive removal of a directory. You're opening yourself up to security holes in your script.

For instance, check out the --one-file-system flag for rm. You probably wouldn't want to remove all the files in any filesystems mounted within mydir...so you would have to include that flag (which has no abbreviation by the way).

Also, what about bind mounts? (See man mount and search for bind.) Those would be files technically on the same filesystem...how would rm -rf handle those? Would it delete them all? That's probably not what you wanted, but to be sure you would have to research it out and test the behavior of rm in relation to bind mounts. There are probably many many other edge cases that I didn't think of.

All this trouble comes about because you don't really want to "remove a directory and its contents"; what you actually want is to have an empty directory to work in.

There's a general principle here—when you specify something other than what you specifically want, because you think that's a route to get what you want, you are usually introducing needless complexity. The thing to do is to find how to specify what you want and then specify that explicitly.*


The better solution, as Jeff Schaller already pointed out in the comments, is to use mktemp, which is actually designed for this purpose. Specifically, from man mktemp:

Create a temporary file or directory, safely, and print its name.

The simplest use of mktemp to create a directory would look like this:

mydir="$(mktemp -d)"

Then you could copy a file into it like so:

cp somefile "$mydir/"

At the end of your script, when you're all done with the directory, you can safely remove it (and whatever you've put in it) by using:

rm -rf "$mydir"

*Another example of this principle is the use of sed commands to handle field-based or column-based files. You may be able to do it...but you can specify exactly what you want by using awk, and with much less complexity.

Wildcard
  • 35,316
  • 26
  • 130
  • 258
  • w/ `sed` you can do it faster, more specifically, and more flexibly. coming from one who has never understood `awk`, i would also argue your *complexity* statement - regexp is *very* basic. but it is also actually a mathematical equation. i dunno if you can get more exact than that. – mikeserv Jan 10 '16 at 04:39
  • 1
    @mikeserv, it's actually funny to me that you claim that `sed` is "more flexible" but in the same breath admit to never having understood `awk`. That's like saying that machine code is "more flexible" than Perl or Lisp (without learning either), because "Perl and Lisp have to be turned into machine code anyways before they are executed by the CPU." `awk` was **designed** to handle field-based data and it is *excellent* for that purpose. – Wildcard Jan 10 '16 at 11:21
  • but that's what regular expressions do - they define matched fields. And anyway, I breathed *twice*. And yeah, I don't understand `awk` - it's way too complicated. I like *simple* expressions - I like to *balance equations*. Regular expressions work that way inherently with *primitives* that build and compound. That's *simple*. `awk` is a gigantic scripting language - it abstracted and confounded every which way. It is far from *simple*. `sed` is more flexible in that it gives you a direct interface to the system's regex libs. *You* call the shots, not the syntax. – mikeserv Jan 10 '16 at 11:42
-1

mkdir fails because it should. That's a good thing. A failing command is a command that is working. It's correctly returning to you a testable error code. And so the simple thing to do is test it.

:& cd -P "${TMPDIR:-/tmp}" || exit
x=$(((z=($$+$!)/2+1000)-1001))
until mkdir -m a=,u=rwx "work.$0.$((x+=1))" &&
      cd -- "work.$0.$x"
do    [ "$x" -lt "$z" ]    || ! break
done  2>/dev/null          &&
WORK=$PWD exec 9<.
mikeserv
  • 57,448
  • 9
  • 113
  • 229
  • 3
    -1 because this is **not** the "simple thing to do." I pray I never have to maintain code written like this. – Wildcard Jan 10 '16 at 11:14
  • @Wildcard - written like what? It's a group of 7 self-evident lines that either obtains a secure temporary working directory and returns true or it doesn't and returns false. What do you think it does? – mikeserv Jan 10 '16 at 11:16
  • 3
    "...7 **self-evident** lines..."—If I hadn't read so many of your answers, I would think that this was trolling. But I do think you honestly believe it. There's no point in discussing it with you when you're so set in your ways. I'll just say this: `mydir="$(mktemp -d)"` could be explained to a kid in grade school in about 30 seconds. Your "self-evident" code requires *vast* knowledge in *excruciating* detail about the inner workings of the shell before it's even possible to *guess* what it does, let alone prove its correctness. Code clarity and code maintainability **does have value**. – Wildcard Jan 10 '16 at 11:31
  • @Wildcard - it says: `cd tmpdir || exit; until mkdir && cd work; do x -lt z || ! break; done && work=$pwd`. It doesn't involve piped data or prngs. There is no inherent difficulty with specific unix systems, or failure to find a command. Can you explain those things to the third grader? In my opinion, that's stuff he ought to know. – mikeserv Jan 10 '16 at 11:44