1. Introduction

Renaming files is a common and recurring task in Linux. However, when doing so manually, it can be daunting to write the full directory path every time, especially if the path is long. Thus, it might be easier if we could rename a file without having to rewrite the directory path.

In this tutorial, we’ll learn how to quickly rename a file with no directory path repetition. In particular, we’ll use the Perl-based rename command and the mv command in combination with brace expansion to rename the files.

The code in this tutorial underwent testing on a Debian 12 (Bookworm) system using GNU Bash 5.1.16.

2. Sample Dataset and Toolset

Before moving forward, let’s make sure that we have all the prerequisites ready, including some files to rename:

$ mkdir -p newdir/subdir/
$ touch newdir/subdir/file1.txt newdir/subdir/file2.csv

The mkdir command creates new directories with specified options. In our case, we make a directory with a subdirectory, newdir/subdir/. The -p option enables us to create parent directories as needed without returning any errors in case the directories already exist.

Next, the touch command creates empty files. In this case, we create newdir/subdir/file1.txt and newdir/subdir/file2.csv.

After creating the files, let’s verify that we have the rename command installed on our system:

$ rename --version
/usr/bin/rename using File::Rename version 1.30, File::Rename::Options version 1.10

In case it’s not already available, we can usually install rename from the local package manager:

$ sudo apt install rename

On Debian and Ubuntu, file-rename is a default alternative to the rename command.

3. Using the rename Command

The behavior of the rename command may differ based on the Linux distribution in use, as there are multiple versions of rename — each with different syntax and functionalities. Two commonly used versions are Perl-based rename and rename from the util-linux package.

3.1. Default Behavior

In this section, we’ll use a Perl-based rename command to rename files.

Let’s rename the files using a Perl expression similar to replacing a substring in a string:

$ rename 's/txt$/sh/' newdir/subdir/file1.txt

The rename command uses the substitution expression (‘s/txt$/sh/’) to replace txt with sh in the given filename, newdir/subdir/file1.txt.

Let’s understand each option used in the substitution expression:

  • s indicates substituion
  • / is the delimiter to separate the search and replacement pattern
  • txt$ is the search pattern that finds the txt string at the end ($) of the filename
  • sh is the replacement string

Furthermore, we can use a global (g) or case-sensitive (i) flag to substitute specific substrings from the given filename.

To print filenames of successfully renamed files, the –verbose (-v) option comes in handy:

$ rename -v 's/1.sh$/_name.txt/' newdir/subdir/file1.sh 
newdir/subdir/file1.sh renamed as newdir/subdir/file_name.txt

As the output shows, the file file1.sh was renamed to file_name.txt.

3.2. Excluding the Directory Components

By default, the rename command enables us to rename the full path (–fullpath or –path), including any directory component:

$ rename -v --path 's/subdir\/file/changed_path/' newdir/subdir/file_name.txt
newdir/subdir/file_name.txt renamed as newdir/changed_path_name.txt

The command produces the same output with or without the –path option. Further, it renames file_name.txt to changed_path_name.txt and moves it to the newdir/ directory from the newdir/subdir/ directory path.

We can exclude the directory names from renaming with the –filename (-d) option:

$ rename -v --filename 's/subdir\/file/changed_path/' newdir/subdir/file2.csv

This command does nothing and keeps the original file unchanged.

Although the –filename option can’t remove a directory component from the filename, it can add a subdirectory component in case it already exists:

$ rename -v --filename 's/changed_path/subdir\/file1/' newdir/changed_path_name.txt 
newdir/changed_path_name.txt renamed as newdir/subdir/file1_name.txt

Additionally, we can use either the –nopath or –nofullpath option instead of the –filename option and still obtain the same result.

Let’s use the global (g) flag within the substitution expression to better understand how –filename behaves:

$ rename -v --filename 's/e/o/g' newdir/subdir/file1_name.txt
newdir/subdir/file1_name.txt renamed as newdir/subdir/filo1_namo.txt

Although the substitution expression specifies to replace e with o globally (g), the –filename option sets the substitution string to only the filename, excluding the directory component. As a result, the two occurrences of e in file1_name.txt were replaced with o, while the –filename option prevented the replacement of e in the newdir directory name.

Without the –filename option, the above command would result in an error, stating “No such file or directory”.

3.3. Other Options

If we’re unsure about the potential result of a rename command, we can use the –nono (-n) option.

The -n option forces the rename command to take no action and only print the names of files to be renamed without actually renaming files:

$ rename -n 's/file/changed_name/' newdir/subdir/file2.csv
rename(newdir/subdir/file2.csv, newdir/subdir/changed_name2.csv)

The output of the rename command with the -n option displays the filename before and after renaming.

Let’s list the contents of the newdir/subdir/ directory to check if the above command renamed the file2.csv:

$ ls newdir/subdir/
filo1_namo.txt  file2.csv

The ls command confirms that the file2.csv remains unchanged and shows that the -n option is a perfect way to display the filenames before renaming them.

Furthermore, we can use the –force (-f) option to overwrite any existing files:

$ rename -v -f 's/file2.csv/filo1_namo.txt/' newdir/subdir/file2.csv
newdir/subdir/file2.csv renamed as newdir/subdir/filo1_namo.txt

This command overwrites the already existing file, newdir/subdir/filo1_namo.txt.

Moreover, we can use the file-rename command instead of the rename command with the same options on Debian and Ubuntu distributions.

4. Using the Brace Expansion

Now, let’s scale back to a more basic renaming mechanism. In addition to moving files and directories, we can use the mv command to rename files.

4.1. Basic Examples

The mv command in combination with brace expansion can be used to rename a file without rewriting the directory path:

$ mv -v newdir/subdir/filo1_namo.{txt,csv}
renamed 'newdir/subdir/filo1_namo.txt' -> 'newdir/subdir/filo1_namo.csv'

The -v option in the mv command sets the output to verbose.

The source file specification, newdir/subdir/file1_name.{txt,csv}, uses a brace expansion to specify multiple file names:

  • newdir/subdir/ is the directory path where the file is located
  • file1_name.{txt,csv} uses brace expansion to specify two file names, file1_name.txt and file1_name.csv

The above command changes the file extension of the file1_name.txt file from txt to csv.

We can use brace expansion anywhere in the filename to rename the given file:

$ mv -v newdir/subdir/filo1_{namo,new}.csv 
renamed 'newdir/subdir/filo1_namo.csv' -> 'newdir/subdir/filo1_new.csv'

This command renames the file from file1_name.csv to file1_new.csv while keeping it in the same directory, newdir/subdir/.

Additionally, we can change the directory path of the target file:

$ mv -v newdir/{subdir/filo1,file2}_new.csv 
renamed 'newdir/subdir/filo1_new.csv' -> 'newdir/file2_new.csv'

The above command renames the file from file1_new.csv to file2_new.csv and moves it to the parent directory, newdir/.

4.2. Prevent Overwriting Files

When a file with an identical name is in the destination directory, the mv command replaces the destination file by default. We can alter this default behavior of the mv command with the –no-clobber (-n) option:

$ touch newdir/file2_old.csv && ls newdir/
file2_new.csv  file2_old.csv  subdir
$ mv -n newdir/file2_{old,new}.csv
$ ls newdir/
file2_new.csv  file2_old.csv  subdir

First, the touch command creates a new file within the newdir/ directory and lists its contents using ls. Then, the mv -n construct prevents overwriting existing files.

The mv command does nothing as we already have a file with an identical name within our destination directory. Finally, we use the ls command to list the contents of newdir/ directory illustrating that the original files remain unchanged.

5. cd, pushd, and popd

To avoid writing the directory path of a file twice when renaming, we can also employ the simple cd command and change the current working directory before the operation:

$ cd newdir/subdir/
$ mv file1.txt file1.sh

However, this leaves the shell in the file’s directory, which may not always be desirable.

We could use cd – but another combination of commands is often more convenient — pushd and popd:

$ pushd newdir/subdir/ && mv file1.sh file1.txt && popd

After this operation, we should be in the directory we were in before the command was run, while file1.sh in newdir/subdir/ should be renamed.

The pushd command switches to the specified directory like cd and inserts this directory to the top of the directory stack. Then, the popd command takes us back to the previous directory while removing the current directory from the stack.

Finally, we can also use a subshell to avoid current directory changes:

$ (cd newdir/subdir/ && mv file1.txt file1.sh)

This operation runs the provided commands by creating a subshell to execute the commands in parentheses. Thus, we don’t need a third command.

Furthermore, we can also define a custom Bash function to rename a file without rewriting the directory path:

mvname() {
    path_name=$(dirname -- "$1")
    target_name="${path_name%/}/$2"
    mv -v -- "$1" "$target_name"
}

The in the mv command specifies the end of command options, so only positional arguments are accepted afterward.

We can even add these lines to our ~/.bashrc file and re-read it to use the mvname function:

$ source ~/.bashrc
$ mvname newdir/subdir/file1.sh file2.txt
renamed 'newdir/subdir/file1.sh' -> 'newdir/subdir/file2.txt'

This operation renames the file1.sh file to file2.txt in the newdir/subdir directory without changing the original directory.

6. Conclusion

In this article, we explored several ways to rename a file without rewriting the directory path.

Initially, we discussed the usage of the rename command and its options to rename files. Then, we used the mv command in combination with the brace expansion to achieve the same goal of renaming files without rewriting the directory path. We also learned how to modify the default behavior of mv and prevent overwriting existing files.

Moreover, we used a simple cd followed by the mv command to rename the file, but this approach is not suitable in case we don’t want to change our current working directory. Thus, we looked at the combination of pushd and popd that uses the directory stack to keep track of changed directories and returns to the starting directory at the end of the operation.

We also discussed the usage of a subshell and a custom Bash function to rename files without rewriting the directory path.

We can select any of these methods to do the job, depending on our preferences and needs.


« 上一篇: Gradle 6.0 的新功能
» 下一篇: $HOME/bin目录的用途