简体   繁体   中英

Bulk renaming files with bash and Perl based on file name

I'm looking to bulk rename files in the current directory only and remove certain strings from the end of file names.

Sample:

foo-bar-(ab-4529111094).txt
foo-bar-foo-bar-(ab-189534).txt
foo-bar-foo-bar-bar-(ab-24937932201).txt

the output should look like this:

foo-bar.txt
foo-bar-foo-bar.txt
foo-bar-foo-bar-bar.txt

I want to remove the string -(ab-2492201) at the end of each file name knowing that the digits can vary in length.

A Perl regex is preferred over modules and without using any utilities and for bash oneliner command is highly preferred.

How to accomplish that in both Perl and Bash Shell on Linux? interested to know both solutions.

Try:

$ rename 's/-\(ab-\d+\)(?=\.txt$)//' *.txt

There's a rename command written in Perl. Its first argument is Perl code describing how to transform a filename. You could use the same s/// command in your own Perl program or one-liner.

If that doesn't work, try prename instead of rename ; there's a different, non-Perl-based, rename command installed on some systems, in which case the Perl one may be called prename .

In bash, you could write something like:

for file in *-\(ab-[0-9]*\)*; do
    newfile="${file/-(ab-[0-9]*)/}"
    mv "$file" "$newfile"
done

When you say under the current directory, do you mean in the current directory, or anywhere in or beaneath the current directory and its descendants?

File::Find is a simple way to do the latter, and is a core module so won't need installing. Like so:

use strict;
use warnings;

use autodie;

use File::Find;

find(\&rename, '.');

sub rename {
  return unless -f;
  my $newname = $_;
  return unless $newname =~ s/-\(ab-[0-9]+\)(\.txt)$/$1/i;
  print "rename $_, $newname\n";
}

Update

This program will rename all the files with the given filename pattern only within the current directory .

Note that the initial open loop is there only to create sample files for renaming.

use strict;
use warnings;

use autodie;

open my $fh, '>', $_ for qw(
  foo-bar-(ab-4529111094).txt
  foo-bar-foo-bar-(ab-189534).txt
  foo-bar-foo-bar-bar-(ab-24937932201).txt
);

for (glob '*.txt') {
  next unless -f;
  my $newname = $_;
  next unless $newname =~ s/-\(ab-[0-9]+\)(\.txt)$/$1/i;
  print "rename $_, $newname\n";
  rename $_, $newname;
}

output

rename foo-bar-(ab-4529111094).txt, foo-bar.txt
rename foo-bar-foo-bar-(ab-189534).txt, foo-bar-foo-bar.txt
rename foo-bar-foo-bar-bar-(ab-24937932201).txt, foo-bar-foo-bar-bar.txt

一个更简单,更短(更好?:)) rename正则表达式:

rename 's@-\(.*?\)@@' foo*.txt

check this:

ls -1 | nawk '/foo-bar-/{old=$0;gsub(/-\(.*\)/,"",$0);system("mv \""old"\" "$0)}'

> ls -1 foo*
foo-bar-(ab-4529111094).txt
foo-bar-foo-bar-(ab-189534).txt
foo-bar-foo-bar-bar-(ab-24937932201).txt

> ls -1 | nawk '/foo-bar-/{old=$0;gsub(/-\(.*\)/,"",$0);system("mv \""old"\" "$0)}'

> ls -1 foo*
foo-bar-foo-bar-bar.txt
foo-bar-foo-bar.txt
foo-bar.txt
> 

For detailed explanation check here

Using Perl Regex to Rename Files

With find , perl , and xargs , you could use this one-liner

find . -type f | perl -pe 'print $_; s/input/output/' | xargs -n2 mv

Results without calling mv should just be

OldName NewName
OldName NewName
OldName NewName

How does it work?

  1. find . -type f find . -type f outputs file paths (or file names...you control what gets processed by regex here!)
  2. -p prints file paths to be processed by regex, -e executes inline script
  3. print $_ prints the original file name first (independent of -p )
  4. -n2 prints two elements per line
  5. mv gets the input of the previous line

Another way using just perl:

perl -E'for (<*.*>){ ($new = $_) =~ s/(^.+?)(-\(.+)(\..*$)/$1$3/; say  $_." -> ".$new}'

( say ... is nice for testing, just replace it with rename $_,$new or rename($_,$new) )

  1. <*.*> read every file in the current directory
  2. ($new = $_) =~ saves the following substitution in $new and leaves $_ as intact
  3. (^.+?) save this match in $1 and non-greedy match from the beginning until...
  4. (-\\(.+) the sequence "-( ...anything..." is found. (this match would be saved in $2)
  5. (\\..*$) save everything from the last "." (period) before the end ($) of the line until and including the end of the line -> into $3
  6. substitute the match with the string generated from $1$3

( you could also do it for a specific directory with perl -E'for (</tmp/my/directory/*.*>){ .....

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM