Kako ponoviti raspon brojeva definiranih varijablama u bash?

Kako ponoviti raspon brojeva u Bashu, kada je raspon postavljen na varijablu?

Znam da to mogu učiniti (to se u dokumentaciji Bash naziva "slijedni izraz" ):

  for i in {1..5}; do echo $i; done 

Što daje:

1
2
3
4
5

Međutim, kako mogu zamijeniti bilo koju od krajnjih točaka raspona s varijablom? Ne radi:

 END=5 for i in {1..$END}; do echo $i; done 

Što ispisuje:

{1..5}

1073
04 окт. set eschercycle 04 list . 2008-10-04 04:38 '08 u 4:38 2008-10-04 04:38
@ 17 odgovora
 for i in $(seq 1 $END); do echo $i; done 

edit: više volim seq nad drugim metodama, jer se stvarno sjećam;)

1206
04 окт. odgovor dao Jiaaro Oct 04 2008-10-04 04:41 '08 u 4:41 am 2008-10-04 04:41

Metoda seq je najjednostavnija, ali Bash ima ugrađenu aritmetičku procjenu.

 END=5 for ((i=1;i<=END;i++)); do echo $i done # ==> outputs 1 2 3 4 5 on separate lines 
border=0

Konstrukcija for ((expr1;expr2;expr3)); radi na isti način kao for (expr1;expr2;expr3) u C i sličnim jezicima, a kao i drugi slučajevi ((expr)) , Bash ih tretira kao aritmetiku.

316
05 окт. odgovor je dat efementnim 05 okt. 2008-10-05 00:43 '08 u 0:43 2008-10-05 00:43

rasprava

Korištenje seq izvrsno, kao što je predložio Jiaaro. Pax Diablo je predložio bash petlju kako bi se izbjeglo pozivanje potprocesa, s dodatnom pogodnošću više memorijskog-friendly ako je $ END prevelika. Zathrus je otkrio tipičnu pogrešku u implementaciji petlje, te je također nagovijestio da se, budući i je i tekstualna varijabla, kontinuirane up-and-down transformacije izvode s odgovarajućim usporavanjem.

cijeli broj aritmetika

Ovo je poboljšana inačica petlje Bash:

 typeset -ii END let END=5 i=1 while ((i<=END)); do echo $i … let i++ done 

Ako je jedina stvar koju želimo je echo , onda možemo napisati echo $((i++)) .

ephemient me naučio nešto: Bash omogućuje for ((expr;expr;expr)) . Budući da nikada nisam pročitao cijelu stranicu za Bash (na primjer, to sam napravio s Korn shell ( ksh ) man stranicom, a to je bilo davno), propustila sam ga.

Dakle,

 typeset -ii END # Let be explicit for ((i=1;i<=END;++i)); do echo $i; done 

čini se da je najučinkovitiji u smislu memorije (nema potrebe dodijeliti memoriju da bi se potrošio seq izlaz, što može biti problem ako je END vrlo velik), iako vjerojatno nije najbrži.

početno pitanje

eschercycle je primijetio da {a..b} Bash notacija radi samo s literalima; prema Baševom priručniku. Prepreku je moguće prevladati uz pomoć jedne (unutarnje) fork() bez exec() (kao što je slučaj sa seq pozivom, druga slika zahtijeva fork + exec):

 for i in $(eval echo "{1..$END}"); do 

I eval i echo su Bash ugrađeni, ali za zamjenu naredbi ( $(…) konstrukcija, fork() je obavezna).

161
04 окт. odgovor je dan tzot 04 okt. 2008-10-04 05:38 '08 u 5:38 AM 2008-10-04 05:38

Zato izvorni izraz ne radi.

Iz ljudskog basha:

Proširenje zagrada se izvodi prije bilo kakvih drugih proširenja i posebnih znakova za druga proširenja kao rezultat. Ovo je strogo tekstualno. Bash ne primjenjuje sintaktičko tumačenje u kontekstu proširenja ili teksta između vitičastih zagrada.

Stoga je proširenje ekstenzija nešto što se radi ranije, kao čisto tekstualna makro operacija, prije proširenja parametra.

Školjke su visoko optimizirani hibridi između makro procesora i formalnih programskih jezika. Da bi se optimizirali tipični slučajevi upotrebe, jezik postaje složeniji i prihvaćaju se neka ograničenja.

preporuka

Predlažem da se pridržavate značajki Posix 1 . To znači korištenje for i in <list>; do for i in <list>; do ako je popis već poznat, u suprotnom se koristi while ili seq , kao u:

 #!/bin/sh limit=4 i=1; while [ $i -le $limit ]; do echo $i i=$(($i + 1)) done # Or ----------------------- for i in $(seq 1 $limit); do echo $i done 


1. Bash je sjajna ljuska i koristim je interaktivno, ali ne stavljam bashizme u svoje skripte. Skripte će možda trebati bržu ljusku, sigurnije, više ugrađeno. Možda će trebati pokrenuti sve što je instalirano kao / bin / sh, a zatim postoje svi uobičajeni argumenti pro-standarda. Sjećaš se ljuske, inače bashdoor?
82
20 апр. Odgovor koji je dao DigitalRoss 20. travnja 2011-04-20 01:32 '11 u 1:32 2011-04-20 01:32

POSIX način

Ako vam je stalo do prenosivosti, koristite primjer iz POSIX standarda :

 i=2 end=5 while [ $i -le $end ]; do echo $i i=$(($i+1)) done 

zaključak:

 2 3 4 5 

Stvari koje nisu POSIX:

44
12 июля '15 в 10:54 2015-07-12 10:54 odgovor je dao Ciro Santilli 改造 中心 六四 事件 法轮功 12. srpnja '15 u 10:54 2015-07-12 10:54

Još jedan sloj neizravnosti:

 for i in $(eval echo {1..$END}); do ∶ 
28
14 марта '11 в 22:51 2011-03-14 22:51 odgovor je dao bobbogo 14. ožujka '11 u 10:51 2011-03-14 22:51

Možete koristiti

 for i in $(seq $END); do echo $i; done 
21
04 окт. Odgovor dao Peter Hoffmann Listopad 04. \ t 2008-10-04 04:41 '08 u 4:41 am 2008-10-04 04:41

Ako koristite BSD / OS X, možete koristiti jot umjesto seq:

 for i in $(jot $END); do echo $i; done 
18
16 марта '11 в 2:29 2011-03-16 02:29 odgovor je dao jefeveizen 16. ožujka 2011. u 2:29 2011-03-16 02:29

Ovo odlično funkcionira u bash :

 END=5 i=1 ; while [[ $i -le $END ]] ; do echo $i ((i = i + 1)) done 
13
04 окт. odgovor dati paxdiablo 04 listopad. 2008-10-04 04:42 '08 u 4:42 2008-10-04 04:42

Ako vam je potreban prefiks koji vam se može svidjeti

  for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done 

što će dati

 07 08 09 10 11 12 
11
03 июня '17 в 23:14 2017-06-03 23:14 odgovor je dan hossbear lipanj 03 '17 u 23:14 2017-06-03 23:14

Ovo je drugi način:

 end=5 for i in $(bash -c "echo {1..${end}}"); do echo $i; done 
6
12 июля '15 в 15:36 2015-07-12 15:36 odgovor je dao Jahid 12. srpnja '15. u 15:36 sati 2015-07-12 15:36

Znam da se ovo pitanje odnosi na bash , ali - samo za pisanje - ksh93 pametniji i implementira ga kako se i očekivalo:

 $ ksh -c 'i=5; for x in {1..$i}; do echo "$x"; done' 1 2 3 4 5 $ ksh -c 'echo $KSH_VERSION' Version JM 93u+ 2012-02-29 $ bash -c 'i=5; for x in {1..$i}; do echo "$x"; done' {1..5} 
6
19 сент. Odgovor koji je dao Adrian Frühwirth 19. rujna 2013-09-19 15:33 '13 u 15:33 2013-09-19 15:33

Zamijenite {} sa (( )) :

 tmpstart=0; tmpend=4; for (( i=$tmpstart; i<=$tmpend; i++ )) ; do echo $i ; done 

produktivnost:

 0 1 2 3 4 
4
12 марта '14 в 3:10 2014-03-12 03:10 odgovor je dao BashTheKeyboard 12. ožujka '14. u 3:10 2014-03-12 03:10

Svi su dobri, ali seq je navodno zastarjela i najviše radi s numeričkim rasponima.

Ako zatvorite za petlju u dvostruke navodnike, početne i završne varijable dereferencirat će se kada odjeknete iz niza, i možete poslati string natrag u BASH za izvršenje. $i mora biti izbjegnut tako da se NE ocjenjuje prije slanja u podskup.

 RANGE_START=a RANGE_END=z echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash 

Ovaj se izlaz može dodijeliti i varijabli:

 VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash` 

Jedini "servisni" resurs koji bi trebao generirati mora biti druga instanca BASH-a, tako da mora biti pogodna za intenzivne operacije.

4
17 авг. odgovor je dat SuperBob 17 aug. 2011-08-17 00:18 '11 u 0:18 2011-08-17 00:18

Ako izvršavate naredbe ljuske, a vi (kao što sam ja) imate fetiš za cjevovod, to je dobro:

seq 1 $END | xargs -I {} echo {}

3
27 марта '17 в 22:32 2017-03-27 22:32 odgovor je dao Alex Spangher 27. ožujka 2006. u 22:32 2017-03-27 22:32

Ako želite biti što bliže sintaksi s izrazom zagrada, isprobajte funkciju range iz bash-tricks ' range.bash .

Na primjer, sve sljedeće radnje učinit će istu stvar kao i echo {1..10} :

 source range.bash one=1 ten=10 range {$one..$ten} range $one $ten range {1..$ten} range {1..10} 

Pokušava zadržati svoju vlastitu bash sintaksu sa što je više moguće "gotchas": ona ne podržava samo varijablu, nego često nepoželjno ponašanje nevažećih raspona koji se prenose kao nizovi (na primjer, for i in {1..a}; do echo $i; done ) također je spriječeno.

Ostali odgovori će u većini slučajeva funkcionirati, ali svi imaju najmanje jedan od sljedećih nedostataka:

  • Mnogi od njih koriste podloške , što može naštetiti performansama i možda neće biti moguće na nekim sustavima.
  • Mnogi se oslanjaju na vanjske programe. Čak i seq je binarna datoteka koja mora biti instalirana za upotrebu, bash se mora učitati i mora sadržavati očekivani program za rad u ovom slučaju. Sveprisutan ili ne, mnogo je više od oslanjanja samo na bash jezik.
  • Rješenja koja koriste samo bash-ove vlastite funkcije, kao što je @ephemient, neće raditi u abecednom rasponu, na primjer {a..z} ; proširenje nosača će biti. Pitanje je bilo o rasponima brojeva, pa je to doslovno.
  • Većina njih nije vizualno slična sintaksi raspona {1..10} s proširenim rasponom, tako da programi koji koriste oba mogu biti malo teži za čitanje.
  • @Bobbogo odgovor koristi poznatu sintaksu, ali čini nešto neočekivano ako $END varijabla nije valjan raspon "bookend" za drugu stranu raspona. Na primjer, ako je END=a , pogreška se neće izvršiti, a vrijednost verbatim {1..a} će se odraziti. Ovo je zadano ponašanje basha, ali je često neočekivano.

Odricanje od odgovornosti: Ja sam autor pridruženog koda.

2
27 июля '17 в 16:30 2017-07-27 16:30 odgovor se daje Zac B 27. srpnja '17. u 16:30 sati 2017-07-27 16:30

Ovo djeluje u Bashu i Kornu, a može ići s viših na niže brojeve. Vjerojatno nije najbrži ili najljepši, ali dobro radi. Procesi i negativi.

 function num_range { # Return a range of whole numbers from beginning value to ending value. # >>> num_range start end # start: Whole number to start with. # end: Whole number to end with. typeset sev s=${1} e=${2} if (( ${e} >= ${s} )); then v=${s} while (( ${v} <= ${e} )); do echo ${v} ((v=v+1)) done elif (( ${e} < ${s} )); then v=${s} while (( ${v} >= ${e} )); do echo ${v} ((v=v-1)) done fi } function test_num_range { num_range 1 3 | egrep "1|2|3" | assert_lc 3 num_range 1 3 | head -1 | assert_eq 1 num_range -1 1 | head -1 | assert_eq "-1" num_range 3 1 | egrep "1|2|3" | assert_lc 3 num_range 3 1 | head -1 | assert_eq 3 num_range 1 -1 | tail -1 | assert_eq "-1" } 
0
11 апр. Odgovor dao Ethan Post 11. travnja 2018-04-11 05:37 '18 u 5:37 2018-04-11 05:37

Ostala pitanja o oznaka ili Postavi pitanje