Building CLI-Based File Renaming Tool With Golang

Wilson Tan
The Startup
Published in
4 min readSep 15, 2020

With the capability to build lightweight yet efficient system, Golang has been getting a lot of attention from software engineers who use it to build backend services, microservices etc as well as DevOps engineers who use it as scripting language. FYI, Kubernetes is written in Go as well.

In this article, we will be looking at how to build a CLI-based tool to rename files in batch using spf13/cobra. While there are other libraries that can be used to build CLI application, Cobra offers the usage of flags, arguments validation, auto-generated help command, suggestion when there is typo and many more.

Let’s get start on building our file renaming tool. Before diving into the code, let’s lay out the requirements:

  • Ability to specify the target folder
  • Ability to scan all files in subfolders
  • Ability to include or exclude files based on name or extension

Root command

First, we start by defining a root command which will be called in the main.go. Also, we can create a folder called cmd where we can store all the command files.

To execute the root command from main.go, we can add the following line in the main() function:

cmd.RootCmd.Execute()

With just a few lines, we can already see the CLI application in action. Simply run go run main.go and we will see auto-generated help message on the root command similar to the picture below:

Auto-generated help message on root command

Sub command

Next, we can add more commands to the root command. For example, in the code below, we are adding sub command rename so we can execute commands as such filezy rename .

Basic rename command

Cobra comes with built in validators that check on the arguments on the command. In the code excerpt above, cobra.ExactArgs(1) ensures that there is only 1 positional argument and throws error if validation fails. One (1) number of argument is exactly what we need where we allow users to key in the new file name.

renameCmd.Flags().StringVarP(&folder, "folder", "f", "./", "Target folder to be scanned")

Cobra also provides simple way to add flags. In the example above, we are adding a flag -f to the renameCmd with default value as ./ . To specify a different folder like /c/folder , we can trigger the command as such: filezy rename [filename] -f /c/folder .

Read files from folder

After specifying a flag to read the folder input, let’s implement the logic to read files from the folder.

Read files from a folder

First, it opens the folder in os.Open(folder) then the Readdir(-1) function returns all the FileInfo from the folder in slice. We then loop through all the FileInfo in the slice and append it to files array if it is a folder. The file name will be used later for renaming purposes.

Read files from subfolder recursively

While the above codes can read the files in the current folder and we can possibly call the function to read the directory again when it hits the subfolder, it involves a lot of codes and this can be solved using path/filepath standard library.

Read files from sub folder recursively

The walk function walks the file tree starting from the folder given (as root) into each file or directory in the tree. Note that it can be quite slow when it comes to a large directory.

Adding filter

Not all of the files scanned shall be renamed and that’s why it’s advisable to add a filter to include or exclude files based on the file name or extension. There are several filters that I can think of, for example based on prefix, suffix, regex pattern and extension.

To ease the process, it will be easier that we can store the file name in separate fields such as base file name and extension. Again, with the use of flags, we can get the input of prefix, suffix, regex or extension.

Filter files based on file name

strings and regexp are the two standard library that provides string comparison on prefix and suffix and string matching based on regex pattern.

Numbering of the files

As we are renaming the files in batch where they can potentially be overwritten, we should come up with a mechanism to add an auto-increment number as suffix to the file name.

Besides that, we should keep the sequence of the files the same as before. To achieve this, simply appending number will mess up the sequence of the files. To give you an example, assuming that there are 20 files to be renamed and the names will be as such: file-1.jpg, file-2.jpg, … … file-10.jpg, file-11.jpg… … file-20.jpg. As a result, file-10.jpg will be sorted before file-2.jpg.

To avoid the sequence being reordered, we can add zero in front of the numbers to standardize the number of digits of the suffix. In the code excerpt below, we create two helper functions: to calculate the number of digits of the total number of files and to get the auto-incremental suffix number.

Getting the auto-incremental suffix

Renaming the files

With the old file name and newly built file name, we can call the following function to rename the files.

os.Rename(<old file name>, <new file name>)

Do note that if there is existing file name that coincides with the new file name, then the existing file will be replaced.

Build and run

We have come to an end and we can build the Go application by running go build. We can then add the folder where the built application is to the PATH so that we can run it from the command prompt.

Source code: https://github.com/wilsontwm/filezy

Thanks and cheers!

--

--