开发者

Shell scripting (regex to move files)

开发者 https://www.devze.com 2023-03-28 21:18 出处:网络
I have a list of files such as: i60.st082313ea.jpg i61.st51249c5e.jpg i62.stef1fe5f2.jpg I would like to rename each file in the directory by decrementing the starting integer (eg. 60, 61, 62) by

I have a list of files such as:

i60.st082313ea.jpg

i61.st51249c5e.jpg

i62.stef1fe5f2.jpg

I would like to rename each file in the directory by decrementing the starting integer (eg. 60, 61, 62) by one.

I've done svn-renaming in the shell using something like the following:

for file in *.xml;
    do svn mv $file `basename $file xml`json;
done;

But when it comes to creating a regular expression, and subtracting 1 开发者_如何学Gofrom part of the file, I'm at a loss. Worth mentioned is that the file could have the expression i[0-9]+ repeated elsewhere in the name, so it would only have to match the leading string.

Any help/tutorials/links would really be appreciated.


for file in *.jpg; do
    newfilename=`echo $file | awk -F '.' '{ OFS=".";print "i" substr($1,2,2)+1, $2, $3}'`
    mv $file $newfilename
done;

NOTE this only works, if the filenames matches your example (e.g. the integers are at the 2nd and 3rd positions, and the file has exactly two .s).

HTH


The great thing is, your input is very regular. So using a regex like

^i([0-9]+)(\..*)$

would start at the beginning of the input, match the 'i' as a necessary character, then match a decimal number, then match the rest of the input, up to the end.

The parentheses make groups available for capturing the matches. If you're matching with bash, the capture groups are available in BASH_REMATCH (an array). With this regex, you have two capture groups: the digits you want to decrement, and the rest of the filename.

To make the new filename, you want to concatenate the character 'i', ${BASH_REMATCH[1]} - 1, and ${BASH_REMATCH[2]}.

If you're not matching with bash, perhaps try with perl? (Plus you'd be able to use \d for digits, a particular favorite of mine.) It's a bit heavier on the processor than sed or awk, but much easier for what you're trying to do.

sed supports backreferences, but to do arithmetic on them, you'd have to invoke a shell command from inside the sed expression (doable). awk doesn't really support backreferences, but gawk has a function to help, gensub.


Without creating a process for each file to rename, and in bash shell (you can learn which shell you are using by typing the command echo $SHELL) , assuming that your file name always begin by 'i', followed by a number, followed by a '.':

ls $.jpg | while read file; do
  post_dot=${file#*.}
  pre_dot=${file%%.*}
  number=${pre_dot#i}
  echo "mv "$file" "i$((number-1)).$post_dot""
done

When you are happy with the printed result, remove 'echo "' at the beginning and the '"' at the end of the mv line. The '"' around $file and i$((number-1)).$post_dot in the mv command are important to account for spaces in the filename.


I wrote a little script I call mv_re although maybe mv_eval or mv_perl would make more sense :

 #!/usr/bin/perl

 foreach (@ARGV) {
    push(@evals,$_);
 }

 sub mv_eval {
     my ($dirname) = @_;
     opendir (my($dh), $dirname) or
         die "Couldn't open dir '$dirname': $!";
     my @fs = readdir $dh;
     foreach $f (@fs) {
         $_ = $f;
         foreach $e (@evals) { eval $e; }
         print "mv '$f' '$_'\n" if ($_ ne $f);
     }
     closedir $dh;
 }

 mv_eval(".");

It treats each argument as a line of perl code and runs them against every file name in the current directly. Any file names changed get appropriate mv commands printed. So mv_re s/www.// writes a script to remove the first www. from every file in the current directory.

Ideally, I should add optional command line arguments for the directory or filespec, alternative commands to mv, and an option to just do it rather than writing the user a script.

0

精彩评论

暂无评论...
验证码 换一张
取 消