For loop with an argument based range

2020-04-17 07:58发布

I want to run certain actions on a group of lexicographically named files (01-09 before 10). I have to use a rather old version of FreeBSD (7.3), so I can't use yummies like echo {01..30} or seq -w 1 30.

The only working solution I found is printf "%02d " {1..30}. However, I can't figure out why can't I use $1 and $2 instead of 1 and 30. When I run my script (bash ~/myscript.sh 1 30) printf says {1..30}: invalid number

AFAIK, variables in bash are typeless, so how can't printf accept an integer argument as an integer?

3条回答
我只想做你的唯一
2楼-- · 2020-04-17 08:33

Bash supports C-style for loops:

s=1
e=30
for i in ((i=s; i<e; i++)); do printf "%02d " "$i"; done

The syntax you attempted doesn't work because brace expansion happens before parameter expansion, so when the shell tries to expand {$1..$2}, it's still literally {$1..$2}, not {1..30}.


The answer given by @Kent works because eval goes back to the beginning of the parsing process. I tend to suggest avoiding making habitual use of it, as eval can introduce hard-to-recognize bugs -- if your command were whitelisted to be run by sudo and $1 were, say, '$(rm -rf /; echo 1)', the C-style-for-loop example would safely fail, and the eval example... not so much.

Granted, 95% of the scripts you write may not be accessible to folks executing privilege escalation attacks, but the remaining 5% can really ruin one's day; following good practices 100% of the time avoids being in sloppy habits.


Thus, if one really wants to pass a range of numbers to a single command, the safe thing is to collect them in an array:

a=( )
for i in ((i=s; i<e; i++)); do a+=( "$i" ); done
printf "%02d " "${a[@]}"
查看更多
干净又极端
3楼-- · 2020-04-17 08:46

I guess you are looking for this trick:

#!/bin/bash
s=1
e=30
printf "%02d "  $(eval echo {$s..$e})
查看更多
Summer. ? 凉城
4楼-- · 2020-04-17 08:48

Ok, I finally got it!

#!/bin/bash
#BSD-only iteration method    
#for day in `jot $1 $2`
for ((day=$1; day<$2; day++))
do
    echo $(printf %02d $day)
done

I initially wanted to use the cycle iterator as a "day" in file names, but now I see that in my exact case it's easier to iterate through normal numbers (1,2,3 etc.) and process them into lexicographical ones inside the loop. While using jot, remember that $1 is the numbers amount, and the $2 is the starting point.

查看更多
登录 后发表回答