0

Given a file path which can exist or non exist, how to create the folder structure and file in bash script?

Example running my script(script name and the file path is passed as the paramter):

./someTask.sh /folder/structure/file.html

I have tried mkdir but it creates a folder named file.html too. Any help would be much appreciated.

popcoder
  • 2,952
  • 6
  • 32
  • 47
  • 1
    Use mkdir and only pass in the directory structure, not the filename? Then just touch the file. – 123 Apr 15 '17 at 11:57
  • 1
    You can also use the install command `install -Dm644 /dev/null /folder/structure/file.html` – 123 Apr 15 '17 at 12:08

3 Answers3

2

Here is a solution that is still quite short, but should not cause problems with relative files or files in the root directory.

crfile()
{
  [[ ! -e $1 ]] && mkdir -p -- "$1" && rmdir -- "$1" && touch -- "$1"
}

Explanations :

  • If existing file (of any type), do not proceed.
  • Create a directory first, with the -p option to create the whole structure
  • If directory creation has not succeeded, do not continue
  • If directory creation has succeeded, then remove the just-created directory (it will be empty), and if for whatever reason removal has failed, then do not continue.
  • If everything has succeeded, touch the path to create a file.

By creating the desired target first as a directory, then removing it and creating as a file, there are no longer special cases with the parent sub-directory path (which is never extracted) or with relative paths.

Fred
  • 6,590
  • 9
  • 20
  • 1
    Yes, this is the simplest solution if one doesn't want to use `dirname` nor go crazy with special cases of `${1%/*}`. Just a note for the really paranoid: if you want this function to work even if the pathname begins with a dash, put an extra argument `--` to `mkdir`, `rmdir` and `touch` before `"$1"`. – Dario Apr 15 '17 at 12:47
  • @Dario Good suggestion, I modified the answer. – Fred Apr 15 '17 at 12:52
  • Thanks @Fred, the solutions works perfectly. For simplicity, i went with dirname. Let me know if my choice has any other drawbacks. – popcoder Apr 15 '17 at 17:16
  • 1
    @popcoder I do not see any major advantage or drawback, go with what seems the simplest for you. Using `dirname`, you can avoid creating and then destroying a directory, which could be seen as cleaner. The main thing (whatever solution you go with) is to make sure you do not need additional error handling, because in some cases these solutions will not work (i.e. file already exists) and you may prefer to control error messages and return codes depending on your needs. – Fred Apr 15 '17 at 22:42
2

Just do the (not soo fancy) but simple:

crfile() {
    dir=$(dirname "$1")
    mkdir -p "$dir" && touch "$1"
}

while read -r path; do
    crfile "$path"
done << EOF
some.txt
./some.txt
../some.txt
.././some.txt
../other/some.txt
/some.txt
/sub/some.txt
/sub/../etc/some.txt
EOF

or if you need shortening

crfile() { mkdir -p "$(dirname "$1")" && touch "$1"; }

from the man mkdir:

-p Create intermediate directories as required. If this option is not specified, the full path prefix of each operand must already exist. On the other hand, with this option specified, no error will be reported if a directory given as an operand already exists. Intermediate directories are created with permission bits of rwxrwxrwx (0777) as modified by the current umask, plus write and search permission for the owner.

Using dirname is much better as trying mangle the path using parameter substitution. read more Bash variable substitution vs dirname and basename

And yes, add the -- if you want - it is good if you expecting files or directories starting with -...

Community
  • 1
  • 1
clt60
  • 62,119
  • 17
  • 107
  • 194
0

As an alternate to dirname you could also use parameter substitution:

mkdir -p "${1%/*}" && touch "$1"
grail
  • 914
  • 6
  • 14
  • 2
    _Mostly_ will work. :) and for the `/some.txt`? :) or for the plain `some.txt` (without any directory?) – clt60 Apr 15 '17 at 13:53