Rename multiple files based on pattern in Unix
Asked Answered
R

27

336

There are multiple files in a directory that begin with prefix fgh, for example:

fghfilea
fghfileb
fghfilec

I want to rename all of them to begin with prefix jkl. Is there a single command to do that instead of renaming each file individually?

Robinette answered 6/7, 2009 at 11:21 Comment(1)
Related question: Better way to rename files based on multiple patterns.Intertidal
U
368

There are several ways, but using rename will probably be the easiest.

Using one version of rename (Perl's rename):

rename 's/^fgh/jkl/' fgh*

Using another version of rename (same as Judy2K's answer):

rename fgh jkl fgh*

You should check your platform's man page to see which of the above applies.

Ululant answered 6/7, 2009 at 11:24 Comment(23)
+1 Didn't even know about rename ... Now I can stop using a for loop with mv and sed ... Thanks!Majors
Are you sure this will work? Or, did you mix up a couple of streams from your mind on the same line?Dicot
Works on my machine (Ubuntu 8.10).Ululant
@Dicot I suppose you're talking to Stephan202 -- Looking at the man page, it really doesn't mention regexes, so you might be right. It still seems to be the right tool: rename fgh jkl fgh* should really do the right thing.Majors
Whichever way -- rename is the answer.Majors
Wow. you're right. The man page on my machine is very different from the one I linked... I'll update the answer in a sec. My apologies!Ululant
You are linking to a different rename then you are showing syntax for unixhelp.ed.ac.uk/CGI/man-cgi?rename is the other oneCranium
@Stephan202, now please don't tell me you also have a perl script called rename :-)Dicot
Good one Hasterkun, i was expecting something like that tripped Stephan.Dicot
@Stephan202: I did not understand rename yet. Now, I can have a look at it. Thank you. :-)Zaidazailer
@Alan, yes, Stephan's line can actually go into the rename man page. He should retain it under his corrected answer.Dicot
Thanks all for clarifying the situation! (@nik: it is written in Perl, yes :)Ululant
I never came across that before. Definitely non-standard. It's also on RHEL5 but not Solaris 10.Camire
Not present on all *nix systems. Not on Max OS X for one, and no package in fink to get it. Haven't looked at MacPorts.Algiers
AFAICT, rename seems to be a Linux specific script or utility. If you care at all about portability, please continue using sed and loops or an inline Perl script.Postimpressionism
brew install rename on OS X :)Outrush
Annoyingly, the one supporting regexes is perl-rename on Arch Linux.Rabies
For those interested, I successfully installed the rename utility on Mac OS/X just by copying the script's source to "/usr/bin/rename" and assigning executable permissions: tips.webdesign10.com/files/rename.pl.txt Working beautifully. :-) Running Mavericks on a MacBook Pro Retina.Probabilism
Append-to-filename example for multiple json files (Mac brew install rename version): rename -AFOO *.json Delete string example: rename -DFOO *.jsonOvermodest
version is definitely important, on CentOS, it is v2.Leatherneck
Had to install it on Windows Subsystem for Linux using apt install. The first syntax works on the WSL version.Solangesolano
There are ALWAYS several ways. :)Artwork
A post to guide you with Perl's rename: unix.stackexchange.com/a/727288/12574Deuteragonist
D
150

This is how sed and mv can be used together to do rename:

for f in fgh*; do mv "$f" $(echo "$f" | sed 's/^fgh/jkl/g'); done

As per comment below, if the file names have spaces in them, quotes may need to surround the sub-function that returns the name to move the files to:

for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done
Dicot answered 6/7, 2009 at 11:38 Comment(10)
Very close. Note that you only want to match the first occurrence of fgh: 's/^fgh/jkl/g' (The caret makes all the difference).Ululant
Just for the sake of precision... You mean "fgh at the beginning of the name", not "the first occurrence of fgh". /^fgh/ will match "fghi", but not "efgh".Rosenarosenbaum
@Stephan, That was a typo on my part (fixed it).Dicot
@Dave: correct. Since only filenames matching fgh* are processed, these notions coincide in this particular case. But indeed I could have been more precise.Ululant
Thanks. very useful on my Mac that hasn't got rename installed by default. Sed does the trick :)Piragua
If you do not have access to "rename" this works great. The code may require quotes if your file names include spaces. for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; doneMccune
The rename command didn't really work. The sed command line above though worked on the first try.Epanodos
@Dicot Without quotes renaming this list of files would throw an error: touch fghfilea fghfileb fghfilec fghfile\ d. I kindly suggest to take into consideration @DaveNelson remarks.Emsmus
I guess there's also no need for the g modifier if you only want to match the first occurrence.Disrespectable
Also, if doing more complex patterns, be aware of sed's regex syntax. For example, sed does not do \d for digits; it does [[:digit:]] or [0-9]; and not +, but \+.Casuist
J
96

rename might not be in every system. so if you don't have it, use the shell this example in bash shell

for f in fgh*; do mv "$f" "${f/fgh/xxx}";done
Jonme answered 7/7, 2009 at 2:22 Comment(6)
In this solution, sed command is not required. This one is simpler than @nik's answer.Buckingham
xxx stands for the replacement? in case of the original poster, that would have been "jkl"Alverson
The cleanest solution of them all.Gloriane
Perhaps point out more emphatically that this is a Bash extension which doesn't exist in POSIX sh or generally in other shells.Cornet
Sweet -- there are so many obscure corners in the Unix world -- never seen this one before.Barbrabarbuda
Thank You! I used it for my requirement as below, for f in ls; do mv $f $f.txt; done And no need to install anything.Victorie
M
47

Using mmv:

mmv "fgh*" "jkl#1"
Misreport answered 21/11, 2013 at 11:17 Comment(7)
Wow, this is an excellent and simple way of solving the problem! Glad to be introduced to mmv, thanks!Craps
Thanks!!! Had never heard of mmv before. Just installed and been playing with it - simple, powerful.Padding
if you want to batch rename recursively use ; in conjunction with #1. example: mmv ";fgh*" "#1jkl#2"Fossette
Very elegant solution!Footstalk
Exactly what I needed. Capture groups with '' and call them back with #1,#2,... EX: mmv "my show ep 1080p.*" "my.show.#1.#2" = my.show.001.aviComeau
most appropriate solutionIneslta
Maybe as a note for the lazy reader: #1 contains just the content of what was matched by *Marchal
P
22

There are many ways to do it (not all of these will work on all unixy systems):

  • ls | cut -c4- | xargs -I§ mv fgh§ jkl§

    The § may be replaced by anything you find convenient. You could do this with find -exec too but that behaves subtly different on many systems, so I usually avoid that

  • for f in fgh*; do mv "$f" "${f/fgh/jkl}";done

    Crude but effective as they say

  • rename 's/^fgh/jkl/' fgh*

    Real pretty, but rename is not present on BSD, which is the most common unix system afaik.

  • rename fgh jkl fgh*

  • ls | perl -ne 'chomp; next unless -e; $o = $_; s/fgh/jkl/; next if -e; rename $o, $_';

    If you insist on using Perl, but there is no rename on your system, you can use this monster.

Some of those are a bit convoluted and the list is far from complete, but you will find what you want here for pretty much all unix systems.

Panzer answered 12/10, 2012 at 9:41 Comment(4)
i love this kind of monster !Adios
yeah, i still have a weak spot for perl too :)Panzer
note that ls output should not be parsed and/or pipedFelicidadfelicie
The Perl monster here happens to work flawlessly if you have spaces in your filenames.Alimony
I
18
rename fgh jkl fgh*
Interinsurance answered 6/7, 2009 at 11:28 Comment(7)
On my machine this produces the error 'Bareword "fgh" not allowed while "strict subs" in use at (eval 1) line 1.'Ululant
@Stephan202, what is your machine?Dicot
Ubuntu 8.10 (perl v5.10.0 / 2009-06-26)Ululant
@Ululant I have the same issue, did you resolve? (7 years later)Wiry
@whossname: sorry, really can't remember. (Slow reply due to holiday.)Ululant
@Ululant I ended up solving this and I've already forgotten what the answer wasWiry
This isn't a Perl command, it's a shell command. "Quit Perl and try again" is the solution to this!Interinsurance
E
16

Using find, xargs and sed:

find . -name "fgh*" -type f -print0 | xargs -0 -I {} sh -c 'mv "{}" "$(dirname "{}")/`echo $(basename "{}") | sed 's/^fgh/jkl/g'`"'

It's more complex than @nik's solution but it allows to rename files recursively. For instance, the structure,

.
├── fghdir
│   ├── fdhfilea
│   └── fghfilea
├── fghfile\ e
├── fghfilea
├── fghfileb
├── fghfilec
└── other
    ├── fghfile\ e
    ├── fghfilea
    ├── fghfileb
    └── fghfilec

would be transformed to this,

.
├── fghdir
│   ├── fdhfilea
│   └── jklfilea
├── jklfile\ e
├── jklfilea
├── jklfileb
├── jklfilec
└── other
    ├── jklfile\ e
    ├── jklfilea
    ├── jklfileb
    └── jklfilec

The key to make it work with xargs is to invoke the shell from xargs.

Emsmus answered 2/6, 2014 at 22:3 Comment(4)
I was going to post something like this, but it would have taken me an hour to get the right commandChkalov
The recursion this answer allows for makes it really robust—I just quickly renamed thousands of files in a deeply-nested hierarchy.Benzidine
This should be the excepted answer.Playboy
No, the command is dangerous. God, please do NOT do that. It will make a mess of your directories.Royden
P
6

Generic command would be

find /path/to/files -name '<search>*' -exec bash -c 'mv $0 ${0/<search>/<replace>}' {} \;

where <search> and <replace> should be replaced with your source and target respectively.

As a more specific example tailored to your problem (should be run from the same folder where your files are), the above command would look like:

find . -name 'gfh*' -exec bash -c 'mv $0 ${0/gfh/jkl}' {} \;

For a "dry run" add echo before mv, so that you'd see what commands are generated:

find . -name 'gfh*' -exec bash -c 'echo mv $0 ${0/gfh/jkl}' {} \;

Progressist answered 27/1, 2021 at 11:21 Comment(0)
T
3

To install the Perl rename script:

sudo cpan install File::Rename

There are two renames as mentioned in the comments in Stephan202's answer. Debian based distros have the Perl rename. Redhat/rpm distros have the C rename.
OS X doesn't have one installed by default (at least in 10.8), neither does Windows/Cygwin.

Thallus answered 14/5, 2013 at 14:2 Comment(0)
T
3

Here's a way to do it using command-line Groovy:

groovy -e 'new File(".").eachFileMatch(~/fgh.*/) {it.renameTo(it.name.replaceFirst("fgh", "jkl"))}'
Tabethatabib answered 6/8, 2013 at 16:47 Comment(0)
C
2
#!/bin/sh

#replace all files ended witn .f77 to .f90 in a directory

for filename in *.f77
do 
    #echo $filename
    #b= echo $filename | cut -d. -f1
    #echo $b    
    mv "${filename}" "${filename%.f77}.f90"    
done
Cholesterol answered 23/6, 2014 at 9:2 Comment(0)
E
2

On Solaris you can try:

for file in `find ./ -name "*TextForRename*"`; do 
    mv -f "$file" "${file/TextForRename/NewText}"
done
Exigent answered 23/12, 2014 at 11:41 Comment(1)
You get half a point for quoting the file names inside the loop, but for file in $(find) is fundamentally flawed and cannot be corrected with quoting. If find returns ./file name with spaces you will get a for loop over ./file, name, with, and spaces and no amount of quoting inside the loop will help (or even be necessary).Cornet
A
2

This script worked for me for recursive renaming with directories/file names possibly containing white-spaces:

find . -type f -name "*\;*" | while read fname; do
    dirname=`dirname "$fname"`
    filename=`basename "$fname"`
    newname=`echo "$filename" | sed -e "s/;/ /g"`
    mv "${dirname}/$filename" "${dirname}/$newname"
done

Notice the sed expression which in this example replaces all occurrences of ; with space . This should of course be replaced according to the specific needs.

Alford answered 21/9, 2019 at 16:43 Comment(0)
A
1

Using renamer:

$ renamer --find /^fgh/ --replace jkl * --dry-run

Remove the --dry-run flag once you're happy the output looks correct.

Albacore answered 23/9, 2013 at 17:22 Comment(0)
G
1

I would recommend using my own script, which solves this problem. It also has options to change the encoding of the file names, and to convert combining diacriticals to precomposed characters, a problem I always have when I copy files from my Mac.

#!/usr/bin/perl

# Copyright (c) 2014 André von Kugland

# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

$help_msg =
"rename.pl, a script to rename files in batches, using Perl
           expressions to transform their names.
Usage:
    rename.pl [options] FILE1 [FILE2 ...]
Where options can be:
    -v                      Verbose.
    -vv                     Very verbose.
    --apply                 Really apply modifications.
    -e PERLCODE             Execute PERLCODE. (e.g. 's/a/b/g')
    --from-charset=CS       Source charset. (e.g. \"iso-8859-1\")
    --to-charset=CS         Destination charset. (e.g. \"utf-8\")
    --unicode-normalize=NF  Unicode normalization form. (e.g. \"KD\")
    --basename              Modifies only the last element of the path.
";

use Encode;
use Getopt::Long;
use Unicode::Normalize 'normalize';
use File::Basename;
use I18N::Langinfo qw(langinfo CODESET);

Getopt::Long::Configure ("bundling");

# ----------------------------------------------------------------------------------------------- #
#                                           Our variables.                                        #
# ----------------------------------------------------------------------------------------------- #

my $apply = 0;
my $verbose = 0;
my $help = 0;
my $debug = 0;
my $basename = 0;
my $unicode_normalize = "";
my @scripts;
my $from_charset = "";
my $to_charset = "";
my $codeset = "";

# ----------------------------------------------------------------------------------------------- #
#                                        Get cmdline options.                                     #
# ----------------------------------------------------------------------------------------------- #

$result = GetOptions ("apply" => \$apply,
                      "verbose|v+" => \$verbose,
                      "execute|e=s" => \@scripts,
                      "from-charset=s" => \$from_charset,
                      "to-charset=s" => \$to_charset,
                      "unicode-normalize=s" => \$unicode_normalize,
                      "basename" => \$basename,
                      "help|h|?" => \$help,
                      "debug" => \$debug);

# If not going to apply, then be verbose.
if (!$apply && $verbose == 0) {
  $verbose = 1;
}

if ((($#scripts == -1)
  && (($from_charset eq "") || ($to_charset eq ""))
  && $unicode_normalize eq "")
  || ($#ARGV == -1) || ($help)) {
  print $help_msg;
  exit(0);
}

if (($to_charset ne "" && $from_charset eq "")
  ||($from_charset eq "" && $to_charset ne "")
  ||($to_charset eq "" && $from_charset eq "" && $unicode_normalize ne "")) {
  $codeset = langinfo(CODESET);
  $to_charset = $codeset if $from_charset ne "" && $to_charset eq "";
  $from_charset = $codeset if $from_charset eq "" && $to_charset ne "";
}

# ----------------------------------------------------------------------------------------------- #
#         Composes the filter function using the @scripts array and possibly other options.       #
# ----------------------------------------------------------------------------------------------- #

$f = "sub filterfunc() {\n    my \$s = shift;\n";
$f .= "    my \$d = dirname(\$s);\n    my \$s = basename(\$s);\n" if ($basename != 0);
$f .= "    for (\$s) {\n";
$f .= "        $_;\n" foreach (@scripts);   # Get scripts from '-e' opt. #
# Handle charset translation and normalization.
if (($from_charset ne "") && ($to_charset ne "")) {
  if ($unicode_normalize eq "") {
    $f .= "        \$_ = encode(\"$to_charset\", decode(\"$from_charset\", \$_));\n";
  } else {
    $f .= "        \$_ = encode(\"$to_charset\", normalize(\"$unicode_normalize\", decode(\"$from_charset\", \$_)));\n"
  }
} elsif (($from_charset ne "") || ($to_charset ne "")) {
    die "You can't use `from-charset' nor `to-charset' alone";
} elsif ($unicode_normalize ne "") {
  $f .= "        \$_ = encode(\"$codeset\", normalize(\"$unicode_normalize\", decode(\"$codeset\", \$_)));\n"
}
$f .= "    }\n";
$f .= "    \$s = \$d . '/' . \$s;\n" if ($basename != 0);
$f .= "    return \$s;\n}\n";
print "Generated function:\n\n$f" if ($debug);

# ----------------------------------------------------------------------------------------------- #
#                 Evaluates the filter function body, so to define it in our scope.               #
# ----------------------------------------------------------------------------------------------- #

eval $f;

# ----------------------------------------------------------------------------------------------- #
#                  Main loop, which passes names through filters and renames files.               #
# ----------------------------------------------------------------------------------------------- #

foreach (@ARGV) {
  $old_name = $_;
  $new_name = filterfunc($_);

  if ($old_name ne $new_name) {
    if (!$apply or (rename $old_name, $new_name)) {
      print "`$old_name' => `$new_name'\n" if ($verbose);
    } else {
      print "Cannot rename `$old_name' to `$new_name'.\n";
    }
  } else {
    print "`$old_name' unchanged.\n" if ($verbose > 1);
  }
}
Galven answered 1/11, 2013 at 11:26 Comment(3)
Note that link-only answers are discouraged, SO answers should be the end-point of a search for a solution (vs. yet another stopover of references, which tend to get stale over time). Please consider adding a stand-alone synopsis here, keeping the link as a reference.Slipover
As predicted by @kleopatra, the link has gotten stale over timeSales
@Sales , not anymore.Amarillo
M
1

Using StringSolver tools (windows & Linux bash) which process by examples:

filter fghfilea ok fghreport ok notfghfile notok; mv --all --filter fghfilea jklfilea

It first computes a filter based on examples, where the input is the file names and the output (ok and notok, arbitrary strings). If filter had the option --auto or was invoked alone after this command, it would create a folder ok and a folder notok and push files respectively to them.

Then using the filter, the mv command is a semi-automatic move which becomes automatic with the modifier --auto. Using the previous filter thanks to --filter, it finds a mapping from fghfilea to jklfilea and then applies it on all filtered files.


Other one-line solutions

Other equivalent ways of doing the same (each line is equivalent), so you can choose your favorite way of doing it.

filter fghfilea ok fghreport ok notfghfile notok; mv --filter fghfilea jklfilea; mv
filter fghfilea ok fghreport ok notfghfile notok; auto --all --filter fghfilea "mv fghfilea jklfilea"
# Even better, automatically infers the file name
filter fghfilea ok fghreport ok notfghfile notok; auto --all --filter "mv fghfilea jklfilea"

Multi-step solution

To carefully find if the commands are performing well, you can type the following:

filter fghfilea ok
filter fghfileb ok
filter fghfileb notok

and when you are confident that the filter is good, perform the first move:

mv fghfilea jklfilea

If you want to test, and use the previous filter, type:

mv --test --filter

If the transformation is not what you wanted (e.g. even with mv --explain you see that something is wrong), you can type mv --clear to restart moving files, or add more examples mv input1 input2 where input1 and input2 are other examples

When you are confident, just type

mv --filter

and voilà! All the renaming is done using the filter.

DISCLAIMER: I am a co-author of this work made for academic purposes. There might also be a bash-producing feature soon.

Musician answered 31/1, 2014 at 13:51 Comment(0)
L
1

It was much easier (on my Mac) to do this in Ruby. Here are 2 examples:

# for your fgh example. renames all files from "fgh..." to "jkl..."
files = Dir['fgh*']

files.each do |f|
  f2 = f.gsub('fgh', 'jkl')
  system("mv #{f} #{f2}")
end

# renames all files in directory from "021roman.rb" to "021_roman.rb"
files = Dir['*rb'].select {|f| f =~ /^[0-9]{3}[a-zA-Z]+/}

files.each do |f|
  f1 = f.clone
  f2 = f.insert(3, '_')
  system("mv #{f1} #{f2}")
end
Larrisa answered 11/5, 2014 at 10:4 Comment(0)
F
1

My version of renaming mass files:

for i in *; do
    echo "mv $i $i"
done |
sed -e "s#from_pattern#to_pattern#g” > result1.sh
sh result1.sh
Flattery answered 25/8, 2014 at 7:1 Comment(2)
I like the ability to verify before running the scriptPhiltre
This breaks magnificently on file names containing spaces, quotes, or other shell metacharacters.Cornet
D
1

Another possible parameter expansion:

for f in fgh*; do mv -- "$f" "jkl${f:3}"; done
Demers answered 13/1, 2019 at 18:49 Comment(0)
C
0

This worked for me using regexp:

I wanted files to be renamed like this:

file0001.txt -> 1.txt
ofile0002.txt -> 2.txt 
f_i_l_e0003.txt -> 3.txt

usig the [a-z|_]+0*([0-9]+.) regexp where ([0-9]+.) is a group substring to use on the rename command

ls -1 | awk 'match($0, /[a-z|\_]+0*([0-9]+.*)/, arr) { print   arr[0]  " "  arr[1] }'|xargs  -l mv

Produces:

mv file0001.txt 1.txt
mv ofile0002.txt 2.txt
mv f_i_l_e0003.txt 3.txt

Another example:

file001abc.txt -> abc1.txt
ofile0002abcd.txt -> abcd2.txt 

ls -1 | awk 'match($0, /[a-z|\_]+0*([0-9]+.*)([a-z]+)/, arr) { print   arr[0]  " "  arr[2] arr[1] }'|xargs  -l mv

Produces:

  mv file001abc.txt abc1.txt
  mv ofile0002abcd.txt abcd2.txt 

Warning, be careful.

Conker answered 6/5, 2016 at 19:56 Comment(0)
B
0

I wrote this script to search for all .mkv files recursively renaming found files to .avi. You can customize it to your neeeds. I've added some other things such as getting file directory, extension, file name from a file path just incase you need to refer to something in the future.

find . -type f -name "*.mkv" | while read fp; do 
fd=$(dirname "${fp}");
fn=$(basename "${fp}");
ext="${fn##*.}";
f="${fn%.*}";
new_fp="${fd}/${f}.avi"
mv -v "$fp" "$new_fp" 
done;
Brettbretz answered 29/5, 2016 at 16:29 Comment(0)
S
0

A generic script to run a sed expression on a list of files (combines the sed solution with the rename solution):

#!/bin/sh

e=$1
shift

for f in $*; do
    fNew=$(echo "$f" | sed "$e")
    mv "$f" "$fNew";
done

Invoke by passing the script a sed expression, and then any list of files, just like a version of rename:

script.sh 's/^fgh/jkl/' fgh*
Stork answered 13/10, 2016 at 4:25 Comment(0)
B
0

You can also use below script. it is very easy to run on terminal...

//Rename multiple files at a time

for file in  FILE_NAME*
do
    mv -i "${file}" "${file/FILE_NAME/RENAMED_FILE_NAME}"
done

Example:-

for file in  hello*
do
    mv -i "${file}" "${file/hello/JAISHREE}"
done
Baseburner answered 15/2, 2017 at 12:53 Comment(0)
L
0

This is an extended version of the find + sed + xargs solution.

Original solutions: this and this.

Requirements: search, prune, regex, rename

  1. I want to rename multiple files in many folders.
  2. Some folders should be pruned/excluded.
  3. I am on cygwin and cannot get perl rename to work, which is required for the most popular solution (and I assume it to be slow, since it does not seem to have a pruning option?)

Solution

  1. Use find to get files effectively (with pruning), and with many customization options.
  2. Use sed for regex replacement.
  3. Use xargs to funnel the result into the final command.

Example 1: rename *.js files but ignore node_modules

This example finds files and echos the found file and the renamed file. For safety reasons, it does not move anything for now. You have to replace echo with mv for that.

set -x # stop on error
set -e # verbose mode (echo all commands)

find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js" | 
  sed -nE "s/(.*)\/my(.*)/& \1\/YOUR\2/p" |
  xargs -n 2 echo  # echo first (replace with `mv` later)

The above script turns this:

./x/y/my-abc.js

Into this:

./x/y/YOUR-abc.js

Breakdown of Solution

  1. find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js"
    • Searches for files (-type f).
    • The -not part excludes (and, importantly does not traverse!) the (notoriously ginormous) node_modules folder.
    • File name must match "*.js".
    • You can add more include and exclude clauses.
    • Refs:
  2. sed -nE "s/(.*)\/my\-(.*\.js)/& \1\/YOUR-\2/p"
    • NOTE: sed always takes some getting used to.
    • -E enables "extended" (i.e. more modern) regex syntax.
    • -n is used in combination with the trailing /p flag: -n hides all results, while /p will print only matching results. This way, we only see/move files that need changing, and ignore all others.
    • Replacement regex with sed (and other regex tools) is always of the format: s/regex/replacement/FLAGS
    • In replacement, the & represents the matched input string. This will be the first argument to mv.
    • Refs:
  3. xargs -n 2 echo
    • Run the command echo with (the first two strings of) the replaced string.
    • Refs: man xargs

Good luck!

Louella answered 1/7, 2022 at 6:57 Comment(0)
O
0

This answer is only a concrete application of what is explained in other answers, I hope it will be useful:

I went in a case where I had to rename some properties files that were in the form CodeAPE_fr.properties to CodeAPE.properties, in a whole directory and I did:

for i in `dir`; do mv $i `echo $i | sed 's/_fr.properties/.properties/g'`; done
Overhear answered 10/6, 2023 at 6:21 Comment(0)
A
0

Here is a slight improvement/adjustment that you need if the filenames have whitespaces AND you want to filter based on the portion of the filename that has the pattern with the whitespaces:

for f in "Filename With Spaces"*; do mv "$f" "$(echo $f | sed 's/^Filename With Spaces/FilenameWithNoMoreSpaces/g')"; done

The key difference in my answer is that you need to put quotation marks around the "Filename With Spaces" if that is the portion of the filename that you want to use as the filter to choose which files to operate on. The previous answer(s) mentioned filename with spaces but then selected files based on jkl* which does not have spaces.

Note: You do not have to replace with FilenameWithNoMoreSpaces. I just wanted to show a typical use case as an example.

If the pattern with the whitespace is in the MIDDLE of the filename, then the adjusted command would follow this pattern:

for f in *"Spaces In Middle of Filename"*; do mv "$f" "$(echo $f | sed 's/Spaces In Middle of Filename/FilenameWithNoMoreSpaces/g')"; done

Notice, I added another asterisk to the beginning of the filename pattern with spaces that is in the double quotes and I removed the carat ^ from the "find" part of the sed command. My apologies if I'm over-explaining. I'm taking into account that some people who read this might be completely new to programming.

Almita answered 10/11, 2023 at 22:29 Comment(0)
U
0

With nautilus:

  • select file want to rename by pattern
  • Press Ctrl-F2
  • Choose "Find and replace text"
  • Enter pattern and the replacementt text
  • Press "Rename"

enter image description here

Unbridled answered 5/12, 2023 at 13:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.