Programster's Blog

Tutorials focusing on Linux, programming, and open source

Moving Files

Moving files is such a common/basic thing we have to do every day in Linux that you're probably wondering why I'm even bothering to write a tutorial on it. Well, there are often important subtleties that are forgotten or need to be learned. Hopefully after reading this tutorial, you will feel more comfortable using the mv command in the CLI rather than just using cut and paste in a file manager which is often a lot slower (especially when you are dealing with codebases of hundreds/thousands of files and have .svn or .git areas).

To move file a.txt to within a directory b, I usually do the following:

mv a.txt b/.

However, the following will work too:

mv a.txt b/

You can even use the * wildcard so if you wanted to move all of your files ending in .txt, then you could do the following.

mv *.txt b/.

This is not making use of regular expressions, so you don't need to worry about the . character being considered anything other than a . character. For example a file called filertxt would not have been moved.

If Files Already Exist

The mv command can take the following options which tell it how to behave if the file (or some of the files) already exist at the destination.

  • --no-clobber (-n) - Don't overwrite existing files.
  • --interactive (-i) - prompt before overwriting existing files.
  • --force (-f) - Just overwrite the files and do not prompt.

If you use --interactive option and choose not to overwrite a file it prompts you for, the original will not be removed after the operation finishes.

If you provide more than one of i,f, or n options, then only the last will be used.

Default Behaviour

The mv command will overwrite existing files at the destination without warning/prompting. This may make you question what is the point of having the -f option then? Having -f allows me to set an alias for mv so that it will default to -i, and then I can use -f later to have the command not prompt me. If you don't want to set an alias, then I would recommend setting the -i option just in case.

alias move="mv -i"

I used move as the alias instead of overriding mv because I don't want to forget that mv will automatically overwrite files when I SSH into a remote server.

Moving Files Between Filesystems

When you are moving files within the same filesystem, the operation is almost instant as you are simply changing the metadata to rename the files and perhaps change some pointers etc. You are not actually moving the content of the files on the physical disk.

However, if you were to use the mv command to move the files from one filesystem to another, (even if it is on the same disk, such as a ZFS dataset), then mv will be moving the content and will try to copy all of the files to the destination before finishing off by deleting the originals. This means it will take a lot longer and work your storage drives. I forgot about this subtlety, so when I recently tried to move files from one ZFS dataset to another in the same pool, I ended up running out of space because mv essentially duplicated all my files and failed to complete because it ran out of space.

Move Files One At A Time

If you need a bunch of files to be moved one at a time to prevent running out of space, then you may wish to consider using rsync instead as shown below:

rsync \
--remove-source-files \
--recursive \
--verbose \
--human-readable \
--progress \
--links \
--copy-unsafe-links \
--owner \
--group \
--perms \
--times \
--force \
--ignore-errors \
--sparse \
--info=progress2 \
/path/to/source \
/path/to/destination

Unfortunately there is no interactive option for when the destination file already exists, so you may wish to just skip these cases by adding the --ignore-existing parameter.

References