Search code examples
regexperlunixfile-rename

Error renaming matching files with perl one liner


I want to rename all the files that match a pattern. This kind of one liners usually work for me, but I'm getting some kind of error.

Kind of file names I have: PMSNpoda5718_1p2g_out_fst.txt SCSNpoda5718_1p2g_out_fst.txt

File names I want: PM-SN_poda5718_1p2g_out_fst.txt SC-SN_poda5718_1p2g_out_fst.txt

My first try was:

ls *fst.txt | perl -ne '/^([A-Z][A-Z])([A-Z][A-Z])(poda.*?$)/; system("mv $_ $1-$2_$3")'

But it didn't work.

Finally I came up with a line that prints exactly what I want

ls *fst.txt | perl -lne '/([a-zA-Z]{2})([a-zA-Z]{2})(poda.*?$)/; print "mv $_ $1-$2_$3"'

prints:

mv PMSNpoda5718_1p2g_out_fst.txt PM-SN_poda5718_1p2g_out_fst.txt
mv SCSNpoda5718_1p2g_out_fst.txt SC-SN_poda5718_1p2g_out_fst.txt

But when I use it in order to rename the files:

ls *fst.txt | perl -lne '/([a-zA-Z]{2})([a-zA-Z]{2})(poda.*?$)/; system("mv $_ $1-$2_$3")'

I get

sh: $'32mPMSNpoda5718_1p2g_out_fst.txt\E[0m': command not found
mv: missing destination file operand after ''$'\033''[01'
Try 'mv --help' for more information.
sh: $'32mSCSNpoda5718_1p2g_out_fst.txt\E[0m': command not found
mv: missing destination file operand after ''$'\033''[01'
Try 'mv --help' for more information.

Again but adding echo

ls *fst.txt | perl -lne '/([a-zA-Z]{2})([a-zA-Z]{2})(poda.*?$)/; system("echo mv $_ $1-$2_$3")'

I get

mv
h: $'32mPMSNpoda5718_1p2g_out_fst.txt\E[0m': command not found
mv
h: $'32mSCSNpoda5718_1p2g_out_fst.txt\E[0m': command not found

Can someone point me which is the error so I can understand what I'm doing wrong?

Thanks!


Solution

  • There is no reason to run the rename via the shell.

    ls -1 *fst.txt | perl -MFile::Copy=move -wlnE'
        $new = s/([a-zA-Z]{2})([a-zA-Z]{2})/$1-$2_/r; move $_, $new'
    

    Note the -1 option on ls, so that you do get one filename per line.

    The 32m and [0m are ANSI escapes, that can set colors (and other properties) for the terminal, often used for shell configuration (for prompt for example). Perhaps your ls is putting out those and mv is getting terribly confused with them? One more example of why it's better to avoid going through the shell, unless necessary of course.

    In some shells the existing aliasing, whereby a command is in fact run with various options (which is why ls output gets "dressed" with those codes), can be suppressed by prepending the command by the backslash, \ls.

    Another option is to reduce reliance on the system as much as possible, and build the file list in the program as well

    perl -MFile::Copy=move -wE'
        for (glob "*fst.txt") { 
            $new = s/([a-zA-Z]{2})([a-zA-Z]{2})/$1-$2_/r;
            move $_, $new or warn "Cant rename $_ to $new: $!" }'
    

    where responsibility for building the correct filelist is now on glob.