Quantcast
Viewing all articles
Browse latest Browse all 13

Renaming multiple files: replacing or truncating varied file extensions

In the previous post, I ran into an issue where Wget saved files to disk verbatim, including query strings/parameters. The files on disk ended up looking like this:

  • wp-includes/js/comment-reply.min.js?ver=6.4.2
  • wp-includes/js/jquery/jquery-migrate.min.js?ver=3.4.1
  • wp-includes/js/jquery/jquery.min.js?ver=3.7.1
  • wp-includes/css/dist/block-library/style.min.css?ver=6.4.2

I wanted to find a way to rename all these files, and truncate the filename after and including the question mark. As an example, to convert jquery.min.js?ver=3.7.1 to jquery.min.js.

I ended up creating a setup script to help test this because I was trying several different methods. The following just creates a directory structure with some realistic examples several directories deep in the hierarchy:

setup.sh

#!/bin/sh

mkdir -p demo/wp-includes/js/jquery
mkdir -p demo/wp-includes/css/dist/block-library
touch demo/wp-includes/js/comment-reply.min.js?ver=6.4.2
touch demo/wp-includes/js/jquery/jquery-migrate.min.js?ver=3.4.1
touch demo/wp-includes/js/jquery/jquery.min.js?ver=3.7.1
touch demo/wp-includes/css/dist/block-library/style.min.css?ver=6.4.2

I used the find command to find all the files in the demo directory (and subdirectories below) that include a question mark in the filename:

find ./demo -type f -name '*\?*'

In case the find command is not something you use every day:

  • -type f indicates it will look only for regular files, not directories or symlinks
  • -name '*\?*' will look for any files with a question mark in the name (preceded or followed by zero or more characters)

Method 1: short shell script
One way to update the filenames is by creating a quick shell script, rename.sh:

rename.sh:

#!/bin/sh

NEWFILE=$(echo "$1" | cut -f1 -d?)
mv "$1" "$NEWFILE"

exit 0

Then I can call the rename.sh shell script from the find command using the -exec or -execdir options:

find ./demo -type f -name '*\?*' -execdir ~/rename.sh '{}' \;

After that, I ran find again to confirm the filenames were changed:

find ./demo -type f
./demo/wp-includes/js/jquery/jquery.min.js
./demo/wp-includes/js/jquery/jquery-migrate.min.js
./demo/wp-includes/js/comment-reply.min.js
./demo/wp-includes/css/dist/block-library/style.min.css

That works, but I felt like this is something that could be achieved with a one-liner.

Method 2: while loop
In this case I passed the output of the find command to a while loop that manipulates the filename and moves the file from the old name to the new name:

find ./demo -type f -name '*\?*' | while read OLDFILE; do NEWFILE=$(echo $OLDFILE | cut -f1 -d?); mv $OLDFILE $NEWFILE; done

While that’s technically a one-liner, I found it clunky. It’s hard to read. I don’t want to say to someone, “Just do this,” and give them 125 characters.

Method 3: Perl
I was previously unaware of this, but ran into Perl’s rename function somewhere on a Stack Exchange site:

find ./demo -type f -name '*\?*' | perl -lne '($old=$_) && s/\?ver=.+$// && rename($old,$_)'

That takes some deciphering:

  • The -l option tells Perl to chomp the input (remove whitespace characters, including the newline)
  • The -n option tells Perl to loop over the input one line at a time
  • The -e option tells Perl to execute or evaluate the given command string (rather than loading and executing Perl code from a file)
  • Recall that in Perl, the default variable is $_. And, if a variable is not specified for an operation, it will operate on the default variable.

Perl seems to have fallen out of favor in the past 10-20 years, but it is still very good at some operations and is preinstalled on most Linux distributions. Still, that’s 93 characters and it’s not immediately evident what the code does.

Method 4: the rename command
There are two (or maybe more) rename commands. One is part of the util-linux package from the Linux Kernel Organization. It is pretty basic, although it can be useful. Another is a Perl-based rename command, which can use regular expressions. You can’t have two commands with the same name, so on some systems the Perl-based rename command is file-rename, on others it is prename (pre = Perl Regular Expressions).

It’s kind of a mess. For more details and discussion, check out:

I find the Perl-based rename command the simplest solution to the problem. It may not be installed by default:

sudo apt install rename

Now the command looks like this:

find ./demo -type f -name '*\?*' -execdir file-rename 's/\?.+$//' '{}' \;

74 characters! That feels more like a one-liner.

On How do I change the extension of multiple files?, a comment suggests using + instead of ; to terminate the find command. Since file-rename can take multiple files, + would pass them all instead of running file-rename over and over:

find ./demo -type f -name '*\?*' -execdir file-rename 's/\?.+$//' '{}' +

In my tests that worked perfectly. The link above points out that for an exceedingly long list of files you may encounter the error message arg list too long. I think you are unlikely to run into that under normal circumstances.

If you have other favorite methods or improvements upon the above, let me know in the comments.


Viewing all articles
Browse latest Browse all 13

Trending Articles