Reputation: 247
I'm trying to move folders from 1 to 05 and assigning numbers to them.
Example:
test-01 => test-221
test-02 => test-222
test-03 => test-223
test-04 => test-224
test-05 => test-225
I've tried this;
for num in $(seq -w $2 $3); {
mv -v "test-$num" "test-$1$num"
}
using it like this;
./script.sh 2 21 25
but I'm getting output;
test-21 => test-221
test-22 => test-222
test-23 => test-223
test-24 => test-224
test-25 => test-225
which is of course wrong, see example :-)
I've also tried it this way;
for a in {1..5}; {
for b in {21..25}; {
echo "$a => $b"
} #b
} #a
But I'm getting repeated output like this;
1 => 21
1 => 22
1 => 23
1 => 24
1 => 25
2 => 21
2 => 22
2 => 23
2 => 24
2 => 25
3 => 21
3 => 22
3 => 23
3 => 24
3 => 25
4 => 21
4 => 22
4 => 23
4 => 24
4 => 25
5 => 21
5 => 22
5 => 23
5 => 24
5 => 25
Upvotes: 1
Views: 445
Reputation: 46893
From all the funny discussions we had in the comments (that was a really funny thread), I more or less (probably less) understood what you want... or rather, I made my own idea of what I believe you want. Let me rephrase what I understood (and please forgive me if this is not what you exactly requested).
You want a script that will take three non-negative numeric arguments X
, Y
and Z
(with possibly leading 0
's) with X<Y
and you want to output test-M => test-N
where:
M
ranges from X
to Y
, left-padded with 0
's so that the number of characters in M
is the max number of characters in X
and Y
N=M+Z
, left-padded with 0
's so that the number of characters in N
is the max number of characters of X
, Y
, Z
and Y+Z
. E.g.,
$ ./script 01 04 00220
test-01 => test-00221
test-02 => test-00222
test-03 => test-00223
test-04 => test-00224
$ ./script 99 101 0
test-099 => test-099
test-100 => test-100
test-101 => test-101
$ ./script 99 101 00000
test-099 => test-00099
test-100 => test-00100
test-101 => test-00101
$ ./script 00 02 99
test-00 => test-099
test-01 => test-100
test-02 => test-101
Also, you want a bash solution so that you can mv
the corresponding files without needing to parse the output of another command.
Here we go, and hopefully you'll find some interesting stuff to dig too (remark, the output is of the form mv -nv xxx yyy
rather than test-x => test-y
; remove the echo
when you're happy with this):
#!/bin/bash
prepend_source=test-
prepend_target=test-
append_source=
append_target=
shopt -s extglob
die() { printf >&2 "%s\n" "$@"; exit 1; }
is_number() { [[ $1 = +([[:digit:]]) ]]; }
is_in_range() { [[ -z ${1//0/} ]] || [[ ${1/#+(0)} = $((10#$1)) ]]; }
maxlength() {
local u l=0 retvar=$1
shift
for i in "$@"; do
u=${#i}
((u>l)) && ((l=u))
done
printf -v "$retvar" "%d" "$l"
}
X=$1
Y=$2
Z=$3
is_number "$X" || die "First argument is not a valid number"
is_number "$Y" || die "Second argument is not a valid number"
is_number "$Z" || die "Third argument is not a valid number"
(( 10#$X <= 10#$Y )) || die "Error: first argument is greater than second"
is_in_range "$X" || die "First argument out of range"
is_in_range "$Y" || die "Second argument out of range"
is_in_range "$Z" || die "Third argument out of range"
(( 10#$Y + 10#$Z >= 0 )) || die "Sum of second and last arguments is out of range"
maxlength "length_s" "$X" "$Y"
maxlength "length_t" "$X" "$Y" "$Z" "$((10#$Y+10#$Z))"
for ((i=10#$X;i<=10#$Y;++i)); do
printf -v source "%s%.${length_s}d%s" "$prepend_source" "$i" "$append_source"
printf -v target "%s%.${length_t}d%s" "$prepend_target" "$((10#$Z+$i))" "$append_target"
# Here we're all done!
echo mv -nv -- "$source" "$target" || die "Problem in mv" # or another error handle
done
I've added the variables prepend_source
, append_source
, prepend_target
, append_target
at the beginning of the script so that you can replace them easily by what you want. You could add option parsings to be able to set them from the command line (left as an exercise, unless you insist I do it for you).
Caveat. The numbers are directly handled by bash, so you must use them from within bash's arithmetic range which is (very likely) on a 64 bits machine: [-9223372036854775808,9223372036854775807]. So there's a lot you can do before reaching that. Now, don't worry, the script will not break if anything goes outside this range because I added explicit checks. If this is truly a limitation, you can always use bc
or dc
instead. The bc
or dc
implementation is left to the reader as an exercise. By the way, this only works with non-negative integers.
Is your solution as robust and general as this one?
Upvotes: 1
Reputation: 11713
Try using awk
#!/bin/bash
awk -v fa=$1 -v fb=$2 -v ta=$3 'BEGIN { for(i=fa;i<=fb;i++) printf "test-%02d => test-%02d\n",i,i+ta }'
Test
$bash -f main.sh 1 5 220
test-01 => test-221
test-02 => test-222
test-03 => test-223
test-04 => test-224
test-05 => test-225
Nice looking multi-line code on OP's request
#!/bin/bash
awk -v fa=$1 -v fb=$2 -v ta=$3 'BEGIN {
for(i=fa;i<=fb;i++)
printf "test-%02d => test-%02d\n",i,i+ta
}'
Now actual script for moving files
#!/bin/bash
awk -v fa=$1 -v fb=$2 -v ta=$3 'BEGIN {
for(i=fa;i<=fb;i++) {
cmd = sprintf ("mv -v test-%02d test-%02d",i ,i+ta);
# print cmd;
system(cmd);
}
}'
Upvotes: 2
Reputation: 247
Thanks to all your guys help this is what I have now;
for i in $(seq -w $1 $2); {
echo "test-$i => test-$((10#$i+$3))"
} #for
and this is the output I get when I run ./script.sh 01 10 220 for example;
test-01 => test-221
test-02 => test-222
test-03 => test-223
test-04 => test-224
test-05 => test-225
test-06 => test-226
test-07 => test-227
test-08 => test-228
test-09 => test-229
test-10 => test-230
This was exactly what I wanted to achieve. Thanks a lot to "Robin Green" and "gniourf_gniourf".
and now moving of directories work correctly ^_^
mv -v "temp/test-$i" "temp/test-$((10#$i+$3))"
output of moved directories is;
‘temp/test-01’ -> ‘temp/test-221’
‘temp/test-02’ -> ‘temp/test-222’
‘temp/test-03’ -> ‘temp/test-223’
‘temp/test-04’ -> ‘temp/test-224’
‘temp/test-05’ -> ‘temp/test-225’
‘temp/test-06’ -> ‘temp/test-226’
‘temp/test-07’ -> ‘temp/test-227’
‘temp/test-08’ -> ‘temp/test-228’
‘temp/test-09’ -> ‘temp/test-229’
‘temp/test-10’ -> ‘temp/test-230’
Problem only occurs when I run; ./script.sh 01 10 0 this is the output I get;
test-01 => test-1
test-02 => test-2
test-03 => test-3
test-04 => test-4
test-05 => test-5
test-06 => test-6
test-07 => test-7
test-08 => test-8
test-09 => test-9
test-10 => test-10
as you can see no leading zeros =(
Upvotes: 0
Reputation: 247
Here is what I have so far;
for i in $(seq -w $1 $2); {
echo "test-$i => test-$((i+$3))"
} #for
output I get by running ./script.sh 01 05 220
test-01 => test-221
test-02 => test-222
test-03 => test-223
test-04 => test-224
test-05 => test-225
so it looks like it matches my example :-)
Upvotes: 0
Reputation: 98108
from_a=1
to_a=5
width_a=2
from_b=220
width_b=3
for a in $(seq $from_a $to_a); do
printf -v file_a "%0"$width_a"d" "$a"
printf -v file_b "%0"$width_b"d" $(($a + $from_b))
echo "test-$file_a => test-$file_b"
done
Upvotes: 1
Reputation: 33093
You only need one loop. But whichever way you count it, the number you use for one purpose is going to be the wrong number for the other purpose. So you need to use arithmetic to get the right number, e.g.:
$(( $num + 220 ))
Upvotes: 0