2014-09-24 3 views
1

Я хочу выполнить определенные действия над группой файлов с лексикографическим названием (01-09 до 10). Мне нужно использовать довольно старую версию FreeBSD (7.3), поэтому я не могу использовать yummies вроде echo {01..30} или seq -w 1 30.Для цикла с диапазоном на основе аргументов

Единственным рабочим решением, которое я нашел, является printf "%02d " {1..30}. Однако я не могу понять, почему я не могу использовать $1 и $2 вместо 1 и 30. Когда я запускаю свой сценарий (bash ~/myscript.sh 1 30) printf говорит {1..30}: invalid number

AFAIK, переменные в bash являются безликими, так как не может printf принять целочисленный аргумент как целое число?

+0

Это не проблема с тем, что printf вообще не примет. Это проблема, потому что разложение параметров еще не произошло, когда происходит расширение фигурной скобки. –

+0

Это связано с BashPitfalls # 33: http://mywiki.wooledge.org/BashPitfalls#for_i_in_.7B1...24n.7D - ссылки могут быть полезны для лучшего понимания того, что происходит при выполнении команды , –

+0

(кстати, это возможно для того, чтобы обозначить переменную как содержащую целое число, в то время как есть очень мало оснований для этого, на самом деле существует _is_ некоторое количество хранимых данных типа). –

ответ

0

Ok, я, наконец, получил его !

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

Я изначально хотел использовать итератор цикла, как «день» в именах файлов, но теперь я вижу, что в моем случае точного легче перебирать нормальные числа (1,2,3 и т.д.) и обрабатывать их в лексикографических внутри цикла. При использовании jot помните, что $1 - это количество номеров, а $2 является отправной точкой.

+0

Что такое 'jot'? Это не стандартная команда POSIX или встроенная bash. –

+1

Предполагая, что он просто печатает серию чисел, начиная с первого аргумента и заканчивая непосредственно перед ее вторым аргументом, устанавливается способ, который работает в любом месте bash: 'for ((day = $ 1; day <$ 2; day ++))'. –

+0

(hmm - похоже, BSDism, доступный на OS X, но не на Linux-аксессуарах, которые я ткнул ... то есть, используя это, вы уменьшаете переносимость вашего сценария). –

0

Я предполагаю, что вы ищете этот трюк:

#!/bin/bash 
s=1 
e=30 
printf "%02d " $(eval echo {$s..$e}) 
0

Bash поддерживает C-стиль для петель:

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

Синтаксис вы пытались не работает, потому что расширение скобка происходит перед тем расширение параметра, поэтому, когда оболочка пытается развернуть {$1..$2}, все еще буквально{$1..$2}, а не {1..30}.


Ответ дается @Kent работает, потому что eval восходит к началу процесса синтаксического анализа. Я склонен предлагать избегать привычного его использования, так как eval can introduce hard-to-recognize bugs - если ваша команда была включена в белый список, которая должна быть запущена sudo, а $1 были, скажем, '$(rm -rf /; echo 1)', пример C-стиля для цикла был бы безопасным сбоем, а пример eval ... не так много.

Конечно, 95% написанных вами сценариев могут быть недоступны для людей, выполняющих атаки эскалации привилегий, но оставшиеся 5% могут действительно испортить свой день; после передовой практики 100% времени избегает быть в неряшливых привычках.


Таким образом, если один действительно хочет передать диапазон чисел в одной команде, сейф, главное, чтобы собрать их в массиве:

a=() 
for i in ((i=s; i<e; i++)); do a+=("$i"); done 
printf "%02d " "${a[@]}" 
Смежные вопросы