1

I know this question has been asked, but I can't find more than one solution, and it does not work for me. Essentially, I'm looking for a bash script that will take a file list that looks like this:

image1.jpg
image2.jpg
image3.jpg

And then make a copy of each one, but number it sequentially backwards. So, the sequence would have three new files created, being:

image4.jpg
image5.jpg
image6.jpg

And yet, image4.jpg would have been an untouched copy of image3.jpg, and image5.jpg an untouched copy of image2.jpg, and so on. I have already tried the solution outlined in this stackoverflow question with no luck. I am admittedly not very far down the bash scripting path, and if I take the chunk of code in the first listed answer and make a script, I always get "2: Syntax error: "(" unexpected" over and over. I've tried changing the syntax with the ( around a bit, but no success ever. So, either I am doing something wrong or there's a better script around.

Sorry for not posting this earlier, but the code I'm using is:

image=( image*.jpg )  
MAX=${#image[*]}  
for i in ${image[*]}  
do  
   num=${i:5:3} # grab the digits  
   compliment=$(printf '%03d' $(echo $MAX-$num | bc))  
   ln $i copy_of_image$compliment.jpg  
done

And I'm taking this code and pasting it into a file with nano, and adding !#/bin/bash as the first line, then chmod +x script and executing in bash via sh script. Of course, in my test runs, I'm using files appropriately titled image1.jpg - but I was also wondering about a way to apply this script to a directory of jpegs, not necessarily titled image(integer).jpg - in my file keeping structure, most of these are a single word, followed by a number, then .jpg, and it would be nice to not have to rewrite the script for each use.

4

2 回答 2

0
  • Maybe your real test data runs from 001 to 300, but here you have image1 2 3, and therefore you extract one, not three digits from the filename. num=${i:5:1}

  • Integer arithmetic can be done in the bash without calling bc

  • ${#image[@]} is more robust than ${#image[*]}, but shouldn't be a difference here.
  • I didn't consult a dictionary, but isn't compliment something for your girl friend? The opposite is complement, isn't it? :)
  • the other command made links - to make copies, call cp.

Code:

#!/bin/bash
image=( image*.jpg )
MAX=${#image[@]}
for i in ${image[@]}
do
    num=${i:5:1}
    complement=$((2*$MAX-$num+1))
    cp $i image$complement.jpg
done 

Most important: If it is bash, call it with bash. Best: do a shebang (as you did), make it executable and call it by ./name . Calling it with sh name will force the wrong interpreter. If you don't make it executable, call it bash name.

于 2012-05-11T08:53:09.210 回答
0

Perhaps something like this. It will work well for something like script image*.jpg where the wildcard matches a set of files which match a regular pattern with monotonously increasing numbers of the same length, and less ideally with a less regular subset of the files in the current directory. It simply assumes that the last file's digit index plus one through the total number of file names is the range of digits to loop over.

#!/bin/sh

# Extract number from final file name
eval lastidx=\$$#
tmp=${lastidx#*[!0-9][0-9]}
lastidx=${lastidx#${lastidx%[0-9]$tmp}}
tmp=${lastidx%[0-9][!0-9]*}
lastidx=${lastidx%${lastidx#$tmp[0-9]}}

num=$(expr $lastidx + $#)
width=${#lastidx}

for f; do
    pref=${f%%[0-9]*}
    suff=${f##*[0-9]}
    # Maybe show a warning if pref, suff, or width changed since the previous file
    printf "cp '$f' '$pref%0${width}i$suff'\\n" $num
    num=$(expr $num - 1)
done |
sh

This is sh-compatible; the expr stuff and the substring extraction up front is ugly but Bourne-compatible. If you are fine with the built-in arithmetic and string manipulation constructs of Bash, converting to that form should be trivial.

(To be explicit, ${var%foo} returns the value of $var with foo trimmed off the end, and ${var#foo} does similar trimming from the beginning of the value. Regular shell wildcard matching operators are available in the expression for what to trim. ${#var} returns the length of the value of $var.)

于 2012-05-11T11:56:08.383 回答