136
votes

I know you can do mkdir to create a directory and touch to create a file, but is there no way to do both operations in one go?

i.e. if I want to do the below when the folder other does not exist:

cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt

Error:

cp: cannot create regular file `/my/other/path/here/cpedthing.txt': No such file or directory

Has anyone come up with a function as a workaround for this?

11
If it's essential that the creation of the file and its directory be atomic, you would have to write a file system that offers this operation. It's not possible with the standard Linux file systems. - Peter G.
@toop I understand that this question is now a year and a half old, but several answers were recently merged into this. If you need to this type of thing very often, you may find my answer useful. (I'd argue more useful than the accepted answer, but I'm not begging for rep here :-) ) - Jonathon Reinhart
@tdammers Q:"How do I do X?" A:"Here's how to do Y" - Henry Henrinson

11 Answers

151
votes

Use && to combine two commands in one shell line:

COMMAND1 && COMMAND2
mkdir -p /my/other/path/here/ && touch /my/other/path/here/cpedthing.txt

Note: Previously I recommended usage of ; to separate the two commands but as pointed out by @trysis it's probably better to use && in most situations because in case COMMAND1 fails COMMAND2 won't be executed either. (Otherwise this might lead to issues you might not have been expecting.)

89
votes

You need to make all of the parent directories first.

FILE=./base/data/sounds/effects/camera_click.ogg

mkdir -p "$(dirname "$FILE")" && touch "$FILE"

If you want to get creative, you can make a function:

mktouch() {
    if [ $# -lt 1 ]; then
        echo "Missing argument";
        return 1;
    fi

    for f in "$@"; do
        mkdir -p -- "$(dirname -- "$f")"
        touch -- "$f"
    done
}

And then use it like any other command:

mktouch ./base/data/sounds/effects/camera_click.ogg ./some/other/file
31
votes

Do it with /usr/bin/install:

install -D /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt

when you don't have a source file:

install -D <(echo 1) /my/other/path/here/cpedthing.txt
15
votes

This is what I would do:

mkdir -p /my/other/path/here && touch $_/cpredthing.txt

Here, the $_ is a variable that represents the last argument to the previous command that we executed in line.

As always if you want to see what the output might be, you can test it by using the echo command, like so:

echo mkdir -p /code/temp/other/path/here && echo touch $_/cpredthing.txt

Which outputs as:

mkdir -p /code/temp/other/path/here
touch /code/temp/other/path/here/cpredthing.txt

As a bonus, you could write multiple files at once using brace expansion, for example:

mkdir -p /code/temp/other/path/here &&
touch $_/{cpredthing.txt,anotherfile,somescript.sh}

Again, totally testable with echo:

mkdir -p /code/temp/other/path/here
touch /code/temp/other/path/here/cpredthing.txt /code/temp/other/path/here/anotherfile /code/temp/other/path/here/somescript.sh
13
votes
#!/bin/sh
for f in "$@"; do mkdir -p "$(dirname "$f")"; done
touch "$@"
11
votes

you can do it in two steps:

mkdir -p /my/other/path/here/
touch /my/other/path/here/cpedthing.txt
2
votes
if [ ! -d /my/other ]
then
   mkdir /my/other/path/here
   cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt
fi
2
votes

as I saw and test in a unix forum this solves the problem

ptouch() {
    for p in "$@"; do
        _dir="$(dirname -- "$p")"
        [ -d "$_dir" ] || mkdir -p -- "$_dir"
    touch -- "$p"
    done
}
1
votes

no need for if then statements... you can do it on a single line usign ;

mkdir -p /my/other/path/here;cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt

-- or on two lines --

mkdir -p /my/other/path/here
cp /my/long/path/here/thing.txt /my/other/path/here/cpedthing.txt

-- the -p prevents error returns if the directory already exists (which is what I came here looking for :))

1
votes

In the special (but not uncommon) case where you are trying to recreate the same directory hierarchy, cp --parents can be useful.

For example if /my/long contains the source files, and my/other already exists, you can do this:

cd /my/long
cp --parents path/here/thing.txt /my/other
-4
votes

if you want simple with only 1 param snippet :

rm -rf /abs/path/to/file;  #prevent cases when old file was a folder
mkdir -p /abs/path/to/file; #make it fist as a dir
rm -rf /abs/path/to/file; #remove the leaf of the dir preserving parents 
touch /abs/path/to/file; #create the actual file