Puis-je utiliser une variable dans une extension d'accolade Bash?

10

Voici une sorte de pseudo-code pour ce que j'essaie d'accomplir:

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

Je peux l'exécuter correctement à partir de la ligne de commande en insérant le nombre réel dans la séquence `{1..n} '. J'ai juste besoin de savoir s'il est possible d'inclure une variable ici et comment s'y prendre.

  • Je l' ai essayé exporting il
  • J'ai essayé de mettre la variable elle-même entre accolades dans la séquence: {1..${numlines}}
  • J'ai essayé de le mettre entre guillemets en espérant qu'il se développerait: {1.."$numlines"}
  • J'ai essayé d'échapper à $:{1..\$numlines}

Dois-je utiliser une set -[something]commande pour que cette variable soit développée? J'ai même essayé certaines formes d'utilisation eval... sans succès.

J'ai juste besoin de savoir s'il y a quelque chose de simple ou obscur qui me manque ou si c'est même possible avant de perdre plus de temps dessus.

Je pourrais mettre au point une manière vraiment, vraiment hackish de le faire pour le faire fonctionner au besoin, mais j'aimerais éviter cela si possible et apprendre la bonne façon de procéder.

rubynorails
la source

Réponses:

11

Malheureusement, il n'y a aucun moyen d'utiliser une variable dans cette expansion (AFAIK), car l'expansion de variable se produit après l'expansion de l'accolade.

Heureusement, il existe un outil qui fait le même travail.

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

seqprovient de GNU coreutils; aucune idée de comment le faire dans POSIX.

Tom Hunt
la source
1
(Plus 1). seqest bon pour les systèmes GNU et, si je me souviens bien, le plus récent OSX. Sur d'autres systèmes BSD, on peut utiliser jot à la place.
John1024
seqmarche parfaitement. Merci beaucoup pour votre réponse rapide.
rubynorails
Cela ne faisait pas partie de ma question - mais quelle est la syntaxe (le cas échéant) pour le faire dans l'ordre inverse, comme {16..1}? $(seq $numlines 1)n'a pas marché. Je suppose que je peux toujours man seq, mais je me demandais simplement si quelqu'un savait par le haut de leur tête.
rubynorails
1
Je viens de comprendre comment le faire à l'envers à partir de ce lien -for i in $(seq $numlines -1 1)
rubynorails
seq ${numlines} -1 0
DopeGhoti
10

Sûr. Si vous voulez une boucle for qui incrémente une variable entière, utilisez la forme de la forboucle qui incrémente une variable entière (ou plus généralement effectue une arithmétique sur la ou les variables de boucle).

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

Cette construction fonctionne en bash (et ksh93 et ​​zsh), mais pas en plain sh. En clair, utilisez une boucle while et la [ … ]construction test ( ).

i=1
while [ "$i" -le "$numlines" ]; do
  
  i=$((i+1))
done
Gilles 'SO- arrête d'être méchant'
la source
8

Si vous devez éviter seq, ce qui, comme le souligne Tom Hunt, semble être la solution habituelle à cela, alors vous pouvezeval certainement le faire (cependant, je ne l'encouragerais pas):

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

Vous pouvez rester POSIX en évitant l'expansion {}, et faites simplement des comparaisons mathématiques et entières sur $numlines:

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

En dehors de Posix, bashet kshet zshont également de style C forboucles:

for((i=0; i<numlines; i++)); do echo $i; done
PSkocik
la source
1
J'apprécie vraiment cette réponse également. Bien que cela seqfonctionne bien pour mon scénario et semble être la solution la plus simple, il est bon de savoir qu'il existe d'autres alternatives (même POSIX). Merci pour cela.
rubynorails
2
Il n'y a vraiment aucune raison d'utiliser eval; si vous avez une extension d'accolade, vous avez la boucle de style C.
chepner
@PSkocik - Si je pouvais choisir 2 réponses, je choisirais aussi celle-ci. Lorsque je suis tombé sur le fait que je devais faire cela à l'envers, votre evalexemple était le plus simple et m'aurait évité d'avoir à chercher une autre façon de le faire en utilisant seq. La whileboucle est un peu volumineuse pour moi. J'aime garder les choses courtes et douces, et je n'ai jamais pu faire fonctionner la forboucle de style C en donnant ila valeur 0 ou 1. Elle n'est jamais revenue correctement et était toujours un peu décalée. Je suis sûr qu'il pourrait être modifié pour fonctionner correctement, mais ce sont certainement des solutions utiles, néanmoins.
rubynorails
L' evalapproche est problématique s'il y a quelque chose de non trivial à l'intérieur du corps de la boucle. J'imagine que ce ne serait pas très lisible si vous deviez imbriquer deux de ces boucles.
kasperd