Czy mogę użyć zmiennej w rozwinięciu nawiasu klamrowego Bash?

10

Poniżej znajduje się pseudo-kod dla tego, co próbuję osiągnąć:

#!/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

Mogę wykonać to dobrze z wiersza poleceń, wstawiając rzeczywistą liczbę w sekwencji `{1..n} '. Muszę tylko wiedzieć, czy można tu dołączyć zmienną i jak to zrobić.

  • Próbowałem exporttego
  • Próbowałem umieścić samą zmienną w nawiasach klamrowych wewnątrz sekwencji: {1..${numlines}}
  • Próbowałem umieścić go w cudzysłowie, mając nadzieję, że rozwinie się: {1.."$numlines"}
  • Próbowałem uciec od $:{1..\$numlines}

Czy muszę używać set -[something]polecenia, aby ta zmienna została rozwinięta? Próbowałem nawet niektórych form używania eval... wszystko bezskutecznie.

Muszę tylko wiedzieć, czy brakuje mi czegoś prostego lub niejasnego, czy też jest to możliwe, zanim stracę na to więcej czasu.

Mógłbym opracować naprawdę bardzo hackerski sposób, aby działał zgodnie z potrzebami, ale chciałbym tego uniknąć, jeśli to w ogóle możliwe, i nauczyć się, jak to zrobić.

rubinorails
źródło

Odpowiedzi:

11

Niestety nie ma możliwości użycia zmiennej w tym rozwinięciu (AFAIK), ponieważ rozszerzenie zmiennej następuje po rozwinięciu nawiasu.

Na szczęście istnieje narzędzie, które wykonuje tę samą pracę.

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

seqpochodzi z GNU coreutils; nie mam pojęcia, jak to zrobić w POSIX.

Tom Hunt
źródło
1
(Plus 1). seqjest dobry dla systemów GNU i, jeśli dobrze pamiętam, najnowszego OSX. W innych systemach BSD można zamiast tego użyć jot .
John1024,
seqdziała świetnie. Dziękuję bardzo za szybką odpowiedź.
rubynorails,
To nie było częścią mojego pytania - ale jaka jest składnia (jeśli w ogóle) do robienia tego w odwrotnej kolejności, na przykład {16..1}? $(seq $numlines 1)nie działał. Chyba zawsze mogę man seq, ale zastanawiam się, czy ktokolwiek wiedział z góry.
rubynorails,
1
Właśnie wymyśliłem, jak to zrobić w odwrotnej kolejności od tego linku -for i in $(seq $numlines -1 1)
rubynorails
seq ${numlines} -1 0
DopeGhoti,
10

Pewnie. Jeśli potrzebujesz pętli for, która inkrementuje zmienną całkowitą, użyj formy forpętli, która inkrementuje zmienną całkowitą (lub, bardziej ogólnie, wykonuje arytmetykę na zmiennej (zmiennych) pętli).

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

Ta konstrukcja działa w bash (i ksh93 i zsh), ale nie w zwykłym sh. W zwykłym sh użyj pętli while i konstrukcji test ( [ … ]).

i=1
while [ "$i" -le "$numlines" ]; do
  
  i=$((i+1))
done
Gilles „SO- przestań być zły”
źródło
8

Jeśli musisz unikać seq, co, jak wskazuje Tom Hunt, wydaje się być zwykłym rozwiązaniem tego problemu, to evalzdecydowanie możesz to zrobić (choć nie zachęciłbym tego):

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

Możesz pozostać POSIX, unikając rozszerzenia {}, i po prostu porównuj matematykę i liczbę całkowitą $numlines:

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

Poza POSIX, basha ksha zshtakże C-stylu forpętle:

for((i=0; i<numlines; i++)); do echo $i; done
PSkocik
źródło
1
Naprawdę doceniam również tę odpowiedź. Chociaż seqdziałał dobrze w moim scenariuszu i wydawał się najprostszym rozwiązaniem, dobrze jest wiedzieć, że istnieją inne (nawet POSIX) alternatywy. Dzięki za to.
rubynorails,
2
Naprawdę nie ma powodu do korzystania eval; jeśli masz rozwinięcie nawiasu, masz pętlę w stylu C.
chepner
@PSkocik - Gdybym mógł wybrać 2 odpowiedzi, wybrałbym również tę. Kiedy natknąłem się na to, że muszę to zrobić odwrotnie, twój evalprzykład był najprostszy i uratowałby mnie od konieczności szukania alternatywnego sposobu zrobienia tego przy użyciu seq. whilePętla jest nieco nieporęczne dla mnie. Lubię sprawić, by wszystko było krótkie i słodkie, i nigdy nie mogłem uruchomić forpętli w stylu C, podając iwartość 0 lub 1. Nigdy nie powróciła poprawnie i zawsze była trochę zła. Jestem pewien, że można go poprawić, aby działał poprawnie, ale mimo to są to zdecydowanie pomocne rozwiązania.
rubynorails,
evalPodejście jest problematyczne, jeśli nie ma nic nietrywialnym wewnątrz ciała pętli. Wyobrażam sobie, że nie byłoby bardzo czytelne, gdybyś potrzebował zagnieździć dwie takie pętle.
kasperd