Moving files on Linux without mv

Sometimes the mv command isn't the best option when you need to move a file. So how else do you do it?
128 readers like this.
Why the operating system matters even more in 2017

Internet Archive Book Images. Modified by Opensource.com. CC BY-SA 4.0

The humble mv command is one of those useful tools you find on every POSIX box you encounter. Its job is clearly defined, and it does it well: Move a file from one place in a file system to another. But Linux is nothing if not flexible, and there are other options for moving files. Using different tools can provide small advantages that fit perfectly with a specific use case.

Before straying too far from mv, take a look at this command’s default results. First, create a directory and generate some files with permissions set to 777:

$ mkdir example
$ touch example/{foo,bar,baz}
$ for i in example/*; do ls /bin > "${i}"; done 
$ chmod 777 example/*

You probably don't think about it this way, but files exist as entries, called index nodes (commonly known as inodes), in a filesystem. You can see what inode a file occupies with the ls command and its --inode option:

$ ls --inode example/foo
7476868 example/foo

As a test, move that file from the example directory to your current directory and then view the file’s attributes:

$ mv example/foo .
$ ls -l -G -g --inode
7476868 -rwxrwxrwx. 1 29545 Aug  2 07:28 foo

As you can see, the original file—along with its existing permissions—has been "moved", but its inode has not changed.

That’s the way the mv tool is programmed to move a file: Leave the inode unchanged (unless the file is being moved to a different filesystem), and preserve its ownership and permissions.

Other tools provide different options.

Copy and remove

On some systems, the move action is a true move action: Bits are removed from one point in the file system and reassigned to another. This behavior has largely fallen out of favor. Move actions are now either attribute reassignments (an inode now points to a different location in your file organization) or amalgamations of a copy action followed by a remove action.

The philosophical intent of this design is to ensure that, should a move fail, a file is not left in pieces.

The cp command, unlike mv, creates a brand new data object in your filesystem. It has a new inode location, and it is subject to your active umask. You can mimic a move using the cp and rm (or trash if you have it) commands:

$ cp example/foo .
$ ls -l -G -g --inode
7476869 -rwxrwxr-x. 29545 Aug  2 11:58 foo
$ trash example/foo

The new foo file in this example got 775 permissions because the location’s umask specifically excludes write permissions:

$ umask
0002

For more information about umask, read Alex Juarez’s article about file permissions.

Cat and remove

Similar to a copy and remove, using the cat (or tac, for that matter) command assigns different permissions when your "moved" file is created. Assuming a fresh test environment with no foo in the current directory:

$ cat example/foo > foo
$ ls -l -G -g --inode
7476869 -rw-rw-r--. 29545 Aug 8 12:21 foo
$ trash example/foo

This time, a new file was created with no prior permissions set. The result is entirely subject to the umask setting, which blocks no permission bit for the user and group (the executable bit is not granted for new files regardless of umask), but it blocks the write (value two) bit from others. The result is a file with 664 permission.

Rsync

The rsync command is a robust multipurpose tool to send files between hosts and file system locations. This command has many options available to it, including the ability to make its destination mirror its source.

You can copy and then remove a file with rsync using the --remove-source-files option, along with whatever other option you choose to perform the synchronization (a common, general-purpose one is --archive):

$ rsync --archive --remove-source-files example/foo .
$ ls example
bar  baz
$ ls -lGgi
7476870 -rwxrwxrwx. 1 seth users 29545 Aug 8 12:23 foo

Here you can see that file permission and ownership was retained, the timestamp was updated, and the source file was removed.

A word of warning: Do not confuse this option for --delete, which removes files from your destination directory. Misusing --delete can wipe out most of your data, and it’s recommended that you avoid this option except in a test environment.

You can override some of these defaults, changing permission and modification settings:

$ rsync --chmod=666 --times \
--remove-source-files example/foo .
$ ls example
bar  baz
$ ls -lGgi
7476871 -rw-rw-r--. 1 seth users 29545 Aug 8 12:55 foo

Here, the destination’s umask is respected, so the --chmod=666 option results in a file with 664 permissions.

The benefits go beyond just permissions, though. The rsync command has many useful options (not the least of which is the --exclude flag so you can exempt items from a large move operation) that make it a more robust tool than the simple mv command. For example, to exclude all backup files while moving a collection of files:

$ rsync --chmod=666 --times \
--exclude '*~' \
--remove-source-files example/foo .

Set permissions with install

The install command is a copy command specifically geared toward developers and is mostly invoked as part of the install routine of software compiling. It’s not well known among users (and I do often wonder why it got such an intuitive name, leaving mere acronyms and pet names for package managers), but install is actually a useful way to put files where you want them.

There are many options for the install command, including --backup and --compare command (to avoid "updating" a newer copy of a file).

Unlike cp and cat, but exactly like mv, the install command can copy a file while preserving its timestamp:

$ install --preserve-timestamp example/foo .
$ ls -l -G -g --inode
7476869 -rwxr-xr-x. 1 29545 Aug  2 07:28 foo
$ trash example/foo

Here, the file was copied to a new inode, but its mtime did not change. The permissions, however, were set to the install default of 755.

You can use install to set the file’s permissions, owner, and group:

$ install --preserve-timestamp \
--owner=skenlon \
--group=dialout \
--mode=666 example/foo .
$ ls -li
7476869 -rw-rw-rw-. 1 skenlon dialout 29545 Aug  2 07:28 foo
$ trash example/foo

Move, copy, and remove

Files contain data, and the really important files contain your data. Learning to manage them wisely is important, and now you have the toolkit to ensure that your data is handled in exactly the way you want.

Do you have a different way of managing your data? Tell us your ideas in the comments.

Tags
Seth Kenlon
Seth Kenlon is a UNIX geek, free culture advocate, independent multimedia artist, and D&D nerd. He has worked in the film and computing industry, often at the same time.

5 Comments

For files, you could also create a hardlink to the file from the new location (within the same mount) and then remove the original file.

I didn't know about "install". Thank you for showing us, Seth.

Re: "Unlike cp and cat, but exactly like mv, the install command can copy a file while preserving its timestamp...."

"cp" will also preserve atime and mtime with the "-p" or "-preserve" options.

AFAIK, any operation, including cp, mv and install, that modifies file metadata will update ctime, and there is no way to preserve ctime.

One of my all time favourites for copying whole directories is cpio, as in

cd /old/source/ && find . | cpio -pdmv /new/destination

Firstly you have full and easy control of what will be found via the plethora of find options and secondly the tool cpio is found on nearly every *NIX System I have encountered so far.

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.