1. Introduction
When working with Linux there may come a time when we’ll need to move multiple files and leave one or more files out. In this article, we’re going to look at several ways through which we can achieve such results.
2. Brute Force or Literal Approach
Let’s start with the brute force or literal approach. Simply put, we list all the files we want to relocate after the mv command, except the unwanted file. Essentially, we use the mv command in its most basic form. Thus, we literally transfer the wanted files without any technique to isolate the unwanted file. This does require that we operate at least within the files’ source directory:
/source_dir$ ls
file1 file2 file3 file4 file5
/source_dir$ mv file1 file2 file3 file4 ~/target_dir/
/source_dir$ ls ~/target_dir/
file1 file2 file3 file4
This method may seem inefficient, however, it comes in handy when you have either one file or fewer files to move than those unwanted, and where regular expressions would be inept or complicate things.
3. Renaming the Unwanted File
Next, we can rename the unwanted file as a hidden file, since mv by default doesn’t detect hidden files. Therefore, after renaming the unwanted file, we’ll use the asterisk, *, regular expression to move the rest of the files:
/source_dir$ mv file5 .file5
/source_dir$ mv * ~/target_dir/
/source_dir$ ls -la
total 0
drwxrwxr-x 2 ubuntu ubuntu 60 Jun 10 03:42 .
drwxr-xr-x 21 ubuntu ubuntu 520 Jun 10 03:25 ..
-rw-rw-r-- 1 ubuntu ubuntu 0 Jun 10 00:57 .file5
After moving the files, we can then rename the hidden file to its original name:
/source_dir$ mv .file5 file5
4. Using the Exclamation Negation Format
The third approach uses the exclamation mark as a prefix before the unwanted filename, enclosed in parenthesis. This instructs the operating system to seek out all other files except the one denoted:
$ mv SOURCE_DIRECTORY/!(unwanted_filename) TARGET_DIRECTORY
To use this format we’ll first need to add the shopt -s extglob command to the .bashrc configuration file. This tells Linux to allow pattern matching on path-name expansion:
$ set shopt -s extglob .bashrc
$ mv source_dir/!(file5) target_dir/
5. Using the Caret Negation Format
The fourth approach is very much like the exclamation negation format. This approach places a caret, ^, before the unwanted file, isolating it from the other files to be relocated:
% mv SOURCE_DIRECTORY/^UNWANTED_FILENAME TARGET_DIRECTORY
However, there’re limitations to using this method. First, this method only works in the z shell, zsh. Second, we need to set the extended global shell option, EXTENDED_GLOB, that is, we’ll run setopt extended_glob from the command line. As with shopt -s extglob above, this tells Linux to allow pattern matching on path-name expansion. With these protocols in place, we’ll place the caret, as a prefix before the unwanted file. The mv command will then transfer all other files apart from the unwanted:
$ zsh
% setopt extended_glob
% mv source_dir/^file5 target_dir
6. Using Enclosed or Piped Inverted Searches
These next sub-topics discuss transferring targeted files after evaluating the isolation of unwanted files inside backticks,“, sub-shells, $(), or piped through xargs -i operations. Backticks and sub-shells create enclosed shell environments that evaluate command statements. Other commands within the same command line use the results of the enclosed statements for their operations. This is command substitution.
When piped into the xargs -i operation, xargs gathers contents from standard input and makes them available to featured commands within the same statement. The -i option truncates or replaces the input separators. Hence, white-space delimiters replace the newlines or spaces which separate the contents within resultant input. The xargs -i operation features an mv {} statement. The curly braces, {}, expand the list of results piped into the xargs -i statement.
6.1. Using an Inverted ls Search
In this method, we simply use the ls command with the -I option to do an inverted search with the unwanted file as the index. This displays all other files except the unwanted file. This command statement is, in turn, evaluated within enclosed backticks. The mv command then transfers the results of the enclosed operation to the target directory. For this method to work we need to be in the files’ source directory:
/source_dir$ mv `ls -I file5` ~/target_dir/
Instead of backticks, we can enclose using a sub-shell:
/source_dir$ mv $(ls -I file5) ~/target_dir/
We can also pipe the result from ls-I unwanted_filename into an xargs-i statement, which transfers the results of the inverted search to the target directory:
/source_dir$ ls -I file5 | xargs -i mv {} ~/target_dir/
6.2. Using an Inverted grep Search
This method uses ls to list the source directory contents and pipe them through grep -v to do an inverted search. The grep -v search uses the unwanted file as an index to list all other files. Now, backticks enclose and evaluate this whole piped operation. Thereafter, the mv command transfers the files listed out of the backtick enclosed operation to the target directory:
/source_dir$ mv `ls | grep -v file5` ~/target_dir/
Instead of backticks, we can enclose using a sub-shell:
/source_dir$ mv $(ls | grep -v file5) ~/target_dir/
We can also pipe the result from grep -v inverted into an xargs-i statement, which transfers the results of the inverted search to the target directory:
/source_dir$ ls | grep -v file5 | xargs -i mv {} ~/target_dir
6.3. Using find with an Inverted grep Search
This technique uses the find command to list the contents of a source directory. There is a-type f argument that instructs the find command to search only for regular files. Then the results are piped into an inverted grep search that uses the unwanted file as an index to list all other files. Backticks enclose and evaluate this entire operation. The mv command then transfers the results of the enclosed operation to the target directory:
$ mv `find source_dir/ -type f | grep -v file5` target_dir/
Instead of backticks, we can enclose using a sub-shell:
$ mv $(find source_dir/ -type f | grep -v file5) target_dir/
We can also pipe the result from inverted find/grep search into an xargs-i statement, which transfers the results of the inverted search to the target directory:
$ find source_dir/ -type f | grep -v file5 | xargs -i mv {} target_dir/
Note: This method is safe to use within a script as it has neither prior setups, protocols to observe, nor a need to be in the source directory.
6.4. Using sed Search and Replace
This method uses backticks to enclose a sed statement that searches out the unwanted file and replaces its display with a null entry. That search is then piped back to an echo command that lists all other contents of the source directory. Hence, listed are all other files apart from the unwanted. Thereafter, the mv command transfers the results of listed contents to the targeted location:
/source_dir$ mv `echo * | sed s:file5::g` ~/target_dir/
Instead of backticks, we can enclose using a sub-shell:
/source_dir$ mv $(echo * | sed s:file5::g) ~/target_dir/
We can also pipe the sed search and replace operation via an xargs -i that features an mv {} statement. We must include curly braces, {}, between xargs -i and mv {} as the piped echo/sed statement yields a string. It’s necessary to expand that string for mv to evaluate it. The curly braces appointed to the mv command, in turn, evaluate the newly expanded content.
/source_dir$ echo * | sed s:file5::g | xargs -i {} mv {} ~/target_dir/
6.5. Using the find Command With xargs
Finally, the find command can be used with the xargs command. This method is yet another technique that is safe to use within a script, as there are no prior setups or protocols, nor any need to be in the source directory. The find command is used to list and move contents of the source directory via an xargs operation.
The find command has several options featured on it. The first is -maxdepth 1 which prohibits reiteration to other folders. The second is -mindepth 1 which prevents the source folder from being included in the results to be transferred. The third option is the -not -name unwanted_filename option that uses the unwanted filename as an index to list the remaining content. The fourth is the option type -f that only includes files in the listed results. Finally the -print0 option that creates the list delimited by null characters, removing any white spaces and/or quotations.
The list is then piped to an xargs -0 operation that gathers the list of filenames and uses mv -t to transfer them to the target directory:
$ find source_dir/ -maxdepth 1 -mindepth 1 -not -name file5 -type f -print0 | xargs -0 mv -t target_dir/
We already know xargs gathers contents from standard input. Option -0 is specified because null characters delimit the input, hence, it’s used because of the -print0 option. mv -t essentially instructs the transfer of the sourced content to the target directory.
We can eliminate xargs -0 and use backticks to enclose the find operation without the -print0, as the results need not null characters to delimit, and then transfer the results to the target directory via the mv command:
$ mv `find source_dir/ -maxdepth 1 -mindepth 1 -not -name file5 -type f` target_dir/
Furthermore, instead of backticks, we can use a sub-shell to enclose the find operation:
$ mv $(find source_dir/ -maxdepth 1 -mindepth 1 -not -name file5 -type f) target_dir/
7. Conclusion
In this article, we looked at several techniques to move files from one directory to another except for an unwanted file. We first noted a literal approach followed by renaming the unwanted file in a hidden file format undetectable to a basic mv command format. Then we explored the use of exclamation and caret negation to isolate the unwanted file.
We then looked at inverted search approaches evaluated within backticks*,* a sub-shell, or piped into xargs -i operations and used the mv command to transfer results. These included a sed search and replace method and a final approach that used the find command to isolate an unwanted file, ensure no reiteration into other folders, and neither include the parent folder in the listed results. These results were then piped into a xargs -0 operation that transferred files using the mv -t command or evaluated within backticks or a sub-shell, then transferred by the mv command.