Kann ich eine Variable in einer Bash-Klammererweiterung verwenden?

10

Unten ist eine Art Pseudocode für das, was ich erreichen möchte:

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

Ich kann es gut über die Befehlszeile ausführen, indem ich die tatsächliche Nummer in die Sequenz "{1..n}" einfüge. Ich muss nur wissen, ob es möglich ist, hier eine Variable einzufügen, und wie es geht.

  • Ich habe versucht , exportes ing
  • Ich habe versucht, die Variable selbst in geschweifte Klammern innerhalb der Sequenz zu setzen: {1..${numlines}}
  • Ich habe versucht, es in doppelte Anführungszeichen zu setzen, in der Hoffnung, dass es sich erweitern würde: {1.."$numlines"}
  • Ich habe versucht, dem zu entkommen $:{1..\$numlines}

Muss ich einen set -[something]Befehl verwenden, damit diese Variable erweitert wird? Ich habe sogar einige Formen der Verwendung ausprobiert eval... alles ohne Erfolg.

Ich muss nur wissen, ob mir etwas Einfaches oder Dunkles fehlt oder ob dies überhaupt möglich ist, bevor ich mehr Zeit damit vergeude.

Ich könnte eine wirklich, wirklich hackige Art und Weise zusammenstellen, damit es nach Bedarf funktioniert, aber ich würde das gerne vermeiden, wenn es überhaupt möglich ist, und lernen, wie man es richtig macht.

Rubynorails
quelle

Antworten:

11

Leider gibt es keine Möglichkeit, eine Variable in dieser Erweiterung (AFAIK) zu verwenden, da die Variablenerweiterung nach der Klammererweiterung erfolgt.

Glücklicherweise gibt es ein Tool, das den gleichen Job macht.

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

seqist von GNU Coreutils; Keine Ahnung, wie es in POSIX geht.

Tom Hunt
quelle
1
(Plus 1). seqist gut für GNU-Systeme und, wenn ich mich richtig erinnere, das neueste OSX. Auf anderen BSD-Systemen kann stattdessen Jot verwendet werden.
John1024
seqfunktioniert perfekt. Vielen Dank für Ihre schnelle Antwort.
Rubynorails
Dies war nicht Teil meiner Frage - aber wie lautet die Syntax (falls vorhanden), um dies in umgekehrter Reihenfolge zu tun, z {16..1}. $(seq $numlines 1)funktioniert nicht. Ich denke, ich kann es immer man seq, aber ich frage mich nur, ob jemand etwas von oben wusste.
Rubynorails
1
for i in $(seq $numlines -1 1)
Ich habe
seq ${numlines} -1 0
DopeGhoti
10

Sicher. Wenn Sie eine for-Schleife wünschen, die eine Ganzzahlvariable inkrementiert, verwenden Sie die Form der forSchleife , die eine Ganzzahlvariable inkrementiert (oder allgemeiner eine Arithmetik für die Schleifenvariablen ausführt).

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

Dieses Konstrukt funktioniert in bash (und ksh93 und zsh), aber nicht in plain sh. Verwenden Sie in plain sh eine while-Schleife und das [ … ]Konstrukt test ( ).

i=1
while [ "$i" -le "$numlines" ]; do
  
  i=$((i+1))
done
Gilles 'SO - hör auf böse zu sein'
quelle
8

Wenn Sie vermeiden müssen seq, was, wie Tom Hunt betont, die übliche Lösung dafür zu sein scheint, dann kann es evaldefinitiv ein (obwohl ich es nicht fördern würde):

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

Sie können POSIX beibehalten, indem Sie die Erweiterung {} vermeiden und einfach mathematische und ganzzahlige Vergleiche durchführen für $numlines:

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

Außerhalb von POSIX, bashund kshund zshauch C-Stil forSchleifen:

for((i=0; i<numlines; i++)); do echo $i; done
PSkocik
quelle
1
Ich schätze diese Antwort auch sehr. Obwohl es seqfür mein Szenario gut funktioniert hat und die einfachste Lösung zu sein schien, ist es gut zu wissen, dass es andere (sogar POSIX) Alternativen gibt. Danke dafür.
Rubynorails
2
Es gibt wirklich keinen Grund zu verwenden eval; Wenn Sie eine Klammererweiterung haben, haben Sie die Schleife im C-Stil.
Chepper
@PSkocik - Wenn ich 2 Antworten wählen könnte, würde ich auch diese wählen. Als ich auf die Tatsache stieß, dass ich dies in umgekehrter Reihenfolge tun musste, war Ihr evalBeispiel das einfachste und hätte mich davor bewahrt, nach einer alternativen Methode suchen zu müssen seq. Die whileSchleife ist für mich etwas sperrig. Ich mag es, die Dinge kurz und bündig zu halten, und ich konnte die C-artige forSchleife nie zum Laufen bringen , indem ich iden Wert 0 oder 1 angab. Sie kehrte nie richtig zurück und war immer ein wenig daneben. Ich bin sicher, es könnte optimiert werden, um richtig zu funktionieren, aber dies sind definitiv hilfreiche Lösungen.
Rubynorails
Der evalAnsatz ist problematisch, wenn sich im Körper der Schleife etwas nicht Triviales befindet. Ich stelle mir vor, es wäre nicht sehr lesbar, wenn Sie zwei solcher Schleifen verschachteln müssten.
Kasperd