3

I have some files with names like this:

01_dpm_gsi_182.sl5
02_dpm_devel_gsi_182.sl5
03_DPM_DSI_181.sl5
04_globus_httpd_122.sl5
05_globus_httpd_client_cgi_132.sl5

How can I rename those files, so that I get some thing like:

01_dpmgsi_s2011e01.sl5
02_dpmdevelgsi_s2011e02.sl5
....
....

The closest I was suggested as like this:

#!/usr/bin/perl -n

if (/^([^_]+)_(.+)_([^.]+)([.].+)$/) {
    my $s = $&;
    my $x = $1;
    my $y = $2;
    my $z = 2011;
    my $e = $4;

    $y =~ s/_//g;

    print "mv $s ${x}_${y}_s${z}e$x$e\n"
}

and then use it like this:

# ls | perl -n reName.pl > output
# bash ./output

Is there any better way or one-liner to do this, possibly using sed/awk? Cheers!!

MacUsers
  • 2,091
  • 3
  • 35
  • 56

6 Answers6

6

There is no need to use a bash script to mv the files, perl can do that. This script below uses File::Copy for that purpose. I have commented out the lines that perform the move, so that the script can be tested with the input first.

Code:

use strict;
use warnings;
use v5.10;

#use File::Copy;
my $year = 2011;
for (@ARGV) {
    my ($pre, @p) = split /_/;
    my $ext = pop @p;
    $ext =~ s/.*\./s${year}e$pre./;
    my $new = join '_', $pre, (join '', @p), $ext;
    say "old: $_";
    say "new: $new";
    say "------";
    #move $_, $new or die $!;
}

Usage:

perl script.pl *.sl5

If you do not have version 5.10, exchange say with print and add a newline.

Output:

old: 01_dpm_gsi_182.sl5
new: 01_dpmgsi_s2011e01.sl5
------
old: 02_dpm_devel_gsi_182.sl5
new: 02_dpmdevelgsi_s2011e02.sl5
------
old: 03_DPM_DSI_181.sl5
new: 03_DPMDSI_s2011e03.sl5
------
old: 04_globus_httpd_122.sl5
new: 04_globushttpd_s2011e04.sl5
------
old: 05_globus_httpd_client_cgi_132.sl5
new: 05_globushttpdclientcgi_s2011e05.sl5
TLP
  • 66,756
  • 10
  • 92
  • 149
2

This GNU sed solution might work for you:

 sed 'h;s/[^_.]*\././;s/_//2g;s/^\(\([^_]*\)_[^.]*\)\./\1_s2011e\2e./;x;G;s/\(.*\)\n/mv -v \1 /' file

Explanation:

Store the original file name in the hold space (HS). Remove the numbers preceeding the file extention. Delete all but the first _. Make the new file name using grouping, back references and added text. Swap to the HS. Append the new file name. Add the mv -v command and delete the embedded \n.

As has been already said file names can trip you up - beware!

potong
  • 55,640
  • 6
  • 51
  • 83
2

omg, NEVER parse the output of ls or build queries that be re-evaluated by a shell. Parsing a filelist (as output by ls) is impossible to get right in the face of whitespace (or whatever is in your $IFS variable) and thus is an awkward solution at ANY TIME. Similarly, building correctly escaped shell commandlines is next to impossible.

To stay with shell (which is an excellent tool for this task nevertheless), use file globbing, which splits the arguments correctly:

for i in ./*
do
    firstpart=${i%%_*}
    num=${firstpart#./}
    middlepart=${i#*_}
    middlepart=${middlepart%_*}
    middlepart=`printf %s "$middlepart" | tr -d _`
    lastpart=s2011e"$num".sl5
    echo mv "$i" "${firstpart}_${middlepart}_${lastpart}"
done

To actually execute, remove the echo in the last line.

Jo So
  • 25,005
  • 6
  • 42
  • 59
  • @Jo So: +1 for the comment about `ls` output, but in this particular case it's no harm. Another +1 for my all time favorite shell script. – MacUsers Dec 07 '11 at 13:50
1

You don't need a separate bash script to do the renames. Perl has a built-in rename function. See http://perldoc.perl.org/functions/rename.html for documentation.

I'm not going to comment on the regex. It might be possible to improve it, but as long as it works correctly, I don't see any need to change it.

Jonathan
  • 1,163
  • 7
  • 12
1

This sort of thing has been covered before and the answer is to use an existing renaming utiliy and not to write your own.

Community
  • 1
  • 1
sorpigal
  • 25,504
  • 8
  • 57
  • 75
  • 1
    The filename manipulation OP is asking for requires a somewhat complicated regex. I think a Perl script is entirely appropriate here. – Jonathan Nov 30 '11 at 14:41
  • @Jonathan: and that's why you use a `rename` written in Perl, as the one mentioned in the linked answer. – ninjalj Nov 30 '11 at 20:30
0

ok, you ask for a one-liner:

ls |xargs -n1|awk -F'[_.]' '{idx=$1;nf=$1"_";for(i=2;i<NF-1;i++)nf=nf$i; nf=nf"_s2011e"idx"."$NF;print "mv "$0" "nf;}'

this will print all mv command for you.

you could check the output, if the target names are all correct. if yes, you just add "|sh" at the end of the line, it will do the rename.

test (without |sh, to see output)

kent$  ls |xargs -n1|awk -F'[_.]' '{idx=$1;nf=$1"_";for(i=2;i<NF-1;i++)nf=nf$i; nf=nf"_s2011e"idx"."$NF;print "mv "$0" "nf;}'
mv 01_dpm_gsi_182.sl5 01_dpmgsi_s2011e01.sl5
mv 02_dpm_devel_gsi_182.sl5 02_dpmdevelgsi_s2011e02.sl5
mv 03_DPM_DSI_181.sl5 03_DPMDSI_s2011e03.sl5
mv 04_globus_httpd_122.sl5 04_globushttpd_s2011e04.sl5
mv 05_globus_httpd_client_cgi_132.sl5 05_globushttpdclientcgi_s2011e05.sl5
Kent
  • 189,393
  • 32
  • 233
  • 301
  • Parsing `ls` output for processing files? This will certainly break on spaces, unusual filenames, etc.. It is not good practice. – sorpigal Nov 30 '11 at 14:38
  • @Sorpigal check OP's example file pls. AND. the part before awk is just for showing how it worked. the point is the awk part, right? you can of course using find, xargs etc. to handle spaces. but not the point of this question. – Kent Nov 30 '11 at 14:42
  • @Kent Isn't it possible to simply use a glob? – TLP Nov 30 '11 at 15:46
  • glob to rename? what did u mean? from OP's perl script, the extension seems to be anything. (I don't know much about perl). again, in my answer "ls|xargs -n1" is just for getting the input of the later awk. – Kent Nov 30 '11 at 16:06
  • And I don't know much about awk. =) But I assume it is possible to simply feed awk arguments somehow besides piping to stdin. `awk ... *`? – TLP Nov 30 '11 at 16:22
  • 1
    @Kent: It doesn't matter how well known your input, bad practice is bad practice. You should have just said `for file in * ; do awk ... "$file" ; done` or, at a minimum, `find . -maxdepth 1 -type f -print0 | xargs -0 ...` etc. There's no excuse for `ls | anything`, IMO. – sorpigal Dec 01 '11 at 11:36