[linux] Is there a way to make mv create the directory to be moved to if it doesn't exist?

So, if I'm in my home directory and I want to move foo.c to ~/bar/baz/foo.c , but those directories don't exist, is there some way to have those directories automatically created, so that you would only have to type

mv foo.c ~/bar/baz/ 

and everything would work out? It seems like you could alias mv to a simple bash script that would check if those directories existed and if not would call mkdir and then mv, but I thought I'd check to see if anyone had a better idea.

This question is related to linux macos unix mkdir mv

The answer is


Save as a script named mv or mv.sh

#!/bin/bash
# mv.sh
dir="$2"
tmp="$2"; tmp="${tmp: -1}"
[ "$tmp" != "/" ] && dir="$(dirname "$2")"
[ -a "$dir" ] ||
mkdir -p "$dir" &&
mv "$@"

Or put at the end of your ~/.bashrc file as a function that replaces the default mv on every new terminal. Using a function allows bash keep it memory, instead of having to read a script file every time.

function mv ()
{
    dir="$2"
    tmp="$2"; tmp="${tmp: -1}"
    [ "$tmp" != "/" ] && dir="$(dirname "$2")"
    [ -a "$dir" ] ||
    mkdir -p "$dir" &&
    mv "$@"
}

These based on the submission of Chris Lutz.


Sillier, but working way:

mkdir -p $2
rmdir $2
mv $1 $2

Make the directory with mkdir -p including a temporary directory that is shares the destination file name, then remove that file name directory with a simple rmdir, then move your file to its new destination. I think answer using dirname is probably the best though.


Code:

if [[ -e $1 && ! -e $2 ]]; then
   mkdir --parents --verbose -- "$(dirname -- "$2")"
fi
mv --verbose -- "$1" "$2"

Example:

arguments: "d1" "d2/sub"

mkdir: created directory 'd2'
renamed 'd1' -> 'd2/sub'

You can chain commands:mkdir ~/bar/baz | mv foo.c ~/bar/baz/
or shell script is here:

#!/bin/bash
mkdir $2 | mv $1 $2

1. Open any text editor
2. Copy-paste shell script.
3. Save it as mkdir_mv.sh
4. Enter your script's directory: cd your/script/directory
5. Change file mode : chmod +x mkdir_mv.sh
6. Set alias: alias mv="./mkdir_mv.sh"
Now, whenever you run command mv moving directory will be created if does not exists.


I frequently stumble upon this issue while bulk moving files to new subdirectories. Ideally, I want to do this:

mv * newdir/  

Most of the answers in this thread propose to mkdir and then mv, but this results in:

mkdir newdir && mv * newdir 
mv: cannot move 'newdir/' to a subdirectory of itself

The problem I face is slightly different in that I want to blanket move everything, and, if I create the new directory before moving then it also tries to move the new directory to itself. So, I work around this by using the parent directory:

mkdir ../newdir && mv * ../newdir && mv ../newdir .

Caveats: Does not work in the root folder (/).


i accomplished this with the install command on linux:

root@logstash:# myfile=bash_history.log.2021-02-04.gz ; install -v -p -D $myfile /tmp/a/b/$myfile

bash_history.log.2021-02-04.gz -> /tmp/a/b/bash_history.log.2021-02-04.gz

the only downside being the file permissions are changed:

root@logstash:# ls -lh /tmp/a/b/

-rwxr-xr-x 1 root root 914 Fev  4 09:11 bash_history.log.2021-02-04.gz

if you dont mind resetting the permission, you can use:

-g, --group=GROUP   set group ownership, instead of process' current group
-m, --mode=MODE     set permission mode (as in chmod), instead of rwxr-xr-x
-o, --owner=OWNER   set ownership (super-user only)

Making use of the tricks in "Getting the last argument passed to a shell script" we can make a simple shell function that should work no matter how many files you want to move:

# Bash only
mvdir() { mkdir -p "${@: -1}" && mv "$@"; }

# Other shells may need to search for the last argument
mvdir() { for last; do true; done; mkdir -p "$last" && mv "$@"; }

((cd src-path && tar --remove-files -cf - files-to-move) | ( cd dst-path && tar -xf -))

Based on a comment in another answer, here's my shell function.

# mvp = move + create parents
function mvp () {
    source="$1"
    target="$2"
    target_dir="$(dirname "$target")"
    mkdir --parents $target_dir; mv $source $target
}

Include this in .bashrc or similar so you can use it everywhere.


You can use mkdir:

mkdir -p ~/bar/baz/ && \
mv foo.c ~/bar/baz/

A simple script to do it automatically (untested):

#!/bin/sh

# Grab the last argument (argument number $#)    
eval LAST_ARG=\$$#

# Strip the filename (if it exists) from the destination, getting the directory
DIR_NAME=`echo $2 | sed -e 's_/[^/]*$__'`

# Move to the directory, making the directory if necessary
mkdir -p "$DIR_NAME" || exit
mv "$@"

My one string solution:

test -d "/home/newdir/" || mkdir -p "/home/newdir/" && mv /home/test.txt /home/newdir/

This will move foo.c to the new directory baz with the parent directory bar.

mv foo.c `mkdir -p ~/bar/baz/ && echo $_`

The -p option to mkdir will create intermediate directories as required.
Without -p all directories in the path prefix must already exist.

Everything inside backticks `` is executed and the output is returned in-line as part of your command.
Since mkdir doesn't return anything, only the output of echo $_ will be added to the command.

$_ references the last argument to the previously executed command.
In this case, it will return the path to your new directory (~/bar/baz/) passed to the mkdir command.


I unzipped an archive without giving a destination and wanted to move all the files except demo-app.zip from my current directory to a new directory called demo-app.
The following line does the trick:

mv `ls -A | grep -v demo-app.zip` `mkdir -p demo-app && echo $_`

ls -A returns all file names including hidden files (except for the implicit . and ..).

The pipe symbol | is used to pipe the output of the ls command to grep (a command-line, plain-text search utility).
The -v flag directs grep to find and return all file names excluding demo-app.zip.
That list of files is added to our command-line as source arguments to the move command mv. The target argument is the path to the new directory passed to mkdir referenced using $_ and output using echo.


$what=/path/to/file;
$dest=/dest/path;

mkdir -p "$(dirname "$dest")";
mv "$what" "$dest"

The simpliest way to do that is:

mkdir [directory name] && mv [filename] $_

Let's suppose I downloaded pdf files located in my download directory (~/download) and I want to move all of them into a directory that doesn't exist (let's say my_PDF).

I'll type the following command (making sure my current working directory is ~/download):

mkdir my_PDF && mv *.pdf $_

You can add -p option to mkdir if you want to create subdirectories just like this: (supposed I want to create a subdirectory named python):

mkdir -p my_PDF/python && mv *.pdf $_

The following shell script, perhaps?

#!/bin/sh
if [[ -e $1 ]]
then
  if [[ ! -d $2 ]]
  then
    mkdir --parents $2
  fi
fi
mv $1 $2

That's the basic part. You might want to add in a bit to check for arguments, and you may want the behavior to change if the destination exists, or the source directory exists, or doesn't exist (i.e. don't overwrite something that doesn't exist).


rsync command can do the trick only if the last directory in the destination path doesn't exist, e.g. for the destination path of ~/bar/baz/ if bar exists but baz doesn't, then the following command can be used:

rsync -av --remove-source-files foo.c ~/bar/baz/

-a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)
-v, --verbose               increase verbosity
--remove-source-files   sender removes synchronized files (non-dir)

In this case baz directory will be created if it doesn't exist. But if both bar and baz don't exist rsync will fail:

sending incremental file list
rsync: mkdir "/root/bar/baz" failed: No such file or directory (2)
rsync error: error in file IO (code 11) at main.c(657) [Receiver=3.1.2]

So basically it should be safe to use rsync -av --remove-source-files as an alias for mv.


It sounds like the answer is no :). I don't really want to create an alias or func just to do this, often because it's one-off and I'm already in the middle of typing the mv command, but I found something that works well for that:

mv *.sh  shell_files/also_with_subdir/ || mkdir -p $_

If mv fails (dir does not exist), it will make the directory (which is the last argument to the previous command, so $_ has it). So just run this command, then up to re-run it, and this time mv should succeed.


mkdir -p `dirname /destination/moved_file_name.txt`  
mv /full/path/the/file.txt  /destination/moved_file_name.txt

You can even use brace extensions:

mkdir -p directory{1..3}/subdirectory{1..3}/subsubdirectory{1..2}      
  • which creates 3 directories (directory1, directory2, directory3),
    • and in each one of them two subdirectories (subdirectory1, subdirectory2),
      • and in each of them two subsubdirectories (subsubdirectory1 and subsubdirectory2).

You have to use bash 3.0 or newer.


Examples related to linux

grep's at sign caught as whitespace How to prevent Google Colab from disconnecting? "E: Unable to locate package python-pip" on Ubuntu 18.04 How to upgrade Python version to 3.7? Install Qt on Ubuntu Get first line of a shell command's output Cannot connect to the Docker daemon at unix:/var/run/docker.sock. Is the docker daemon running? Run bash command on jenkins pipeline How to uninstall an older PHP version from centOS7 How to update-alternatives to Python 3 without breaking apt?

Examples related to macos

Problems with installation of Google App Engine SDK for php in OS X dyld: Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib error running php after installing node with brew on Mac Could not install packages due to an EnvironmentError: [Errno 13] How do I install Java on Mac OSX allowing version switching? Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) Can't compile C program on a Mac after upgrade to Mojave You don't have write permissions for the /Library/Ruby/Gems/2.3.0 directory. (mac user) How can I install a previous version of Python 3 in macOS using homebrew? Could not install packages due to a "Environment error :[error 13]: permission denied : 'usr/local/bin/f2py'"

Examples related to unix

Docker CE on RHEL - Requires: container-selinux >= 2.9 What does `set -x` do? How to find files modified in last x minutes (find -mmin does not work as expected) sudo: npm: command not found How to sort a file in-place How to read a .properties file which contains keys that have a period character using Shell script gpg decryption fails with no secret key error Loop through a comma-separated shell variable Best way to find os name and version in Unix/Linux platform Resource u'tokenizers/punkt/english.pickle' not found

Examples related to mkdir

How to create nested directories using Mkdir in Golang? Bash mkdir and subfolders How to create a directory and give permission in single command Recursive mkdir() system call on Unix How to create new folder? How to mkdir only if a directory does not already exist? mkdir -p functionality in Python Is there a way to make mv create the directory to be moved to if it doesn't exist?

Examples related to mv

How to move or copy files listed by 'find' command in unix? Is it possible to move/rename files in Git and maintain their history? Is there a way to make mv create the directory to be moved to if it doesn't exist? How to rename with prefix/suffix?