Могу ли я использовать переменную в расширении Bash Brace?

10

Ниже приведен некоторый псевдокод для того, что я пытаюсь выполнить:

#!/bin/bash

# I already have the variable below figured out (positive integer):
numlines=$([returns number of lines containing specific characters in a file])

# This is basically what I want to do with it:
for i in {1..$numlines}; do
    # the part below is already figured out as well:        
    do some other stuff
done

Я могу выполнить его из командной строки, вставив фактическое число в последовательность `{1..n} '. Мне просто нужно знать, можно ли сюда включить переменную и как это сделать.

  • Я попробовал exportэто
  • Я попытался поместить саму переменную в фигурные скобки внутри последовательности: {1..${numlines}}
  • Я попытался поместить это в двойные кавычки, надеясь, что это расширится: {1.."$numlines"}
  • Я пытался избежать $:{1..\$numlines}

Нужно ли использовать set -[something]команду для расширения этой переменной? Я даже попробовал некоторые формы использования eval... все безрезультатно.

Мне просто нужно знать, есть ли что-то простое или неясное, что я пропускаю, или это вообще возможно, прежде чем я потрачу на это больше времени.

Я мог бы создать действительно очень по- хакерский способ сделать это так, чтобы он работал по мере необходимости, но я бы хотел этого избежать, если это вообще возможно, и научиться правильно делать это.

rubynorails
источник

Ответы:

11

К сожалению, нет никакой возможности использовать переменную в этом расширении (AFAIK), так как расширение переменной происходит после расширения скобки.

К счастью, есть инструмент, который выполняет ту же работу.

for i in $(seq 1 $numlines); do
    # stuff
done

seqиз GNU coreutils; понятия не имею, как это сделать в POSIX.

Том Хант
источник
1
(Плюс 1). seqхорошо для систем GNU и, если я правильно помню, самый последний OSX. В других системах BSD вместо этого можно использовать jot .
John1024
seqработает отлично. Большое спасибо за ваш быстрый ответ.
Rubynorails
Это не было частью моего вопроса - но каков синтаксис (если есть) для выполнения этого в обратном порядке, например {16..1}? $(seq $numlines 1)не сработало. Я думаю, что могу всегда man seq, но просто интересно, если бы кто-нибудь знал с макушкой.
Rubynorails
1
Просто разобрался, как сделать это в обратном направлении по этой ссылке -for i in $(seq $numlines -1 1)
rubynorails
seq ${numlines} -1 0
DopeGhoti
10

Конечно. Если вам нужен цикл for, который увеличивает целочисленную переменную, используйте форму forцикла, которая увеличивает целочисленную переменную (или, в более общем случае, выполняет арифметику для переменной (переменных) цикла).

for ((i=1; i<=numlines; i++)); do  done

Эта конструкция работает в bash (и ksh93 и zsh), но не в обычном sh. В обычном sh используйте цикл while и конструкцию test ( [ … ]).

i=1
while [ "$i" -le "$numlines" ]; do
  
  i=$((i+1))
done
Жиль "ТАК - прекрати быть злым"
источник
8

Если вы должны избегать seq, что, как указывает Том Хант, кажется, является обычным решением этой проблемы, тогда evalопределенно можно сделать это (хотя я бы не поощрял это):

eval 'for i in {1..'$numlines'}; do echo $i; done'

Вы можете остаться в POSIX, избежав раскрытия {}, и просто выполните математические и целочисленные сравнения $numlines:

while [ ! "$numlines" -eq 0 ]; do
     echo "$numlines"
     : $((numlines-=1))
done

Вне POSIX, bashа kshи zshтакже имеет C-стиль forпетля:

for((i=0; i<numlines; i++)); do echo $i; done
PSkocik
источник
1
Я тоже очень ценю этот ответ. Хотя он seqотлично работал для моего сценария и казался самым простым решением, полезно знать, что есть и другие (даже POSIX) альтернативы. Спасибо за это.
Rubynorails
2
Там действительно нет причин для использования eval; если у вас есть расширение скобки, у вас есть цикл в стиле C.
Chepner
@PSkocik - Если бы я мог выбрать 2 ответа, я бы также выбрал этот. Когда я столкнулся с тем, что мне нужно сделать это в обратном порядке, ваш evalпример был самым простым и избавил меня от необходимости искать альтернативный способ сделать это, используя seq. whileПетля немного громоздким для меня. Мне нравится, чтобы все было коротким и приятным, и я никогда не мог заставить forцикл в стиле C работать, давая iзначение 0 или 1. Он никогда не возвращался правильно и всегда был немного не в порядке. Я уверен, что он может быть настроен для правильной работы, но это определенно полезные решения, тем не менее.
Rubynorails
evalПодход является проблематичным , если есть что - нибудь нетривиальные внутри тела цикла. Я полагаю, что было бы не очень читабельным, если бы вам нужно было вложить два таких цикла.
Касперд