Linux: copy and create destination dir if it does not exist
Solution 1:
mkdir -p "$d" && cp file "$d"
(there's no such option for cp
).
Solution 2:
If both of the following are true:
- You are using the GNU version of
cp
(and not, for instance, the Mac version), and - You are copying from some existing directory structure and you just need it recreated
then you can do this with the --parents
flag of cp
. From the info page (viewable at http://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html#cp-invocation or with info cp
or man cp
):
--parents Form the name of each destination file by appending to the target directory a slash and the specified name of the source file. The last argument given to `cp' must be the name of an existing directory. For example, the command: cp --parents a/b/c existing_dir copies the file `a/b/c' to `existing_dir/a/b/c', creating any missing intermediate directories.
Example:
/tmp $ mkdir foo
/tmp $ mkdir foo/foo
/tmp $ touch foo/foo/foo.txt
/tmp $ mkdir bar
/tmp $ cp --parents foo/foo/foo.txt bar
/tmp $ ls bar/foo/foo
foo.txt
Solution 3:
Short Answer
To copy myfile.txt
to /foo/bar/myfile.txt
, use:
mkdir -p /foo/bar && cp myfile.txt $_
How does this work?
There's a few components to this, so I'll cover all the syntax step by step.
The mkdir utility, as specified in the POSIX standard, makes directories. The -p
argument, per the docs, will cause mkdir to
Create any missing intermediate pathname components
meaning that when calling mkdir -p /foo/bar
, mkdir will create /foo
and /foo/bar
if /foo
doesn't already exist. (Without -p
, it will instead throw an error.
The &&
list operator, as documented in the POSIX standard (or the Bash manual if you prefer), has the effect that cp myfile.txt $_
only gets executed if mkdir -p /foo/bar
executes successfully. This means the cp
command won't try to execute if mkdir
fails for one of the many reasons it might fail.
Finally, the $_
we pass as the second argument to cp
is a "special parameter" which can be handy for avoiding repeating long arguments (like file paths) without having to store them in a variable. Per the Bash manual, it:
expands to the last argument to the previous command
In this case, that's the /foo/bar
we passed to mkdir
. So the cp
command expands to cp myfile.txt /foo/bar
, which copies myfile.txt
into the newly created /foo/bar
directory.
Note that $_
is not part of the POSIX standard, so theoretically a Unix variant might have a shell that doesn't support this construct. However, I don't know of any modern shells that don't support $_
; certainly Bash, Dash, and zsh all do.
A final note: the command I've given at the start of this answer assumes that your directory names don't have spaces in. If you're dealing with names with spaces, you'll need to quote them so that the different words aren't treated as different arguments to mkdir
or cp
. So your command would actually look like:
mkdir -p "/my directory/name with/spaces" && cp "my filename with spaces.txt" "$_"