Appliquer l'expansion de l'accolade dans «l'ordre inverse»

21

Par exemple, se {a..c}{1..3}développe en a1 a2 a3 b1 b2 b3 c1 c2 c3.

Si je voulais imprimer a1 b1 c1 a2 b2 c2 a3 b3 c3, existe-t-il une manière analogue de le faire? Quelle est la manière la plus simple?

RUBEN GONÇALO MOROUÇO
la source

Réponses:

30

Vous pourriez faire:

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

Ce qui indique ensuite au shell d'évaluer:

echo {a..c}1 {a..c}2 {a..c}3
Stéphane Chazelas
la source
10

Pour ce cas particulier, je pense que l'option donnée par Stéphane Chazelas est la meilleure.

D'un autre côté, lorsque vous développez des choses plus complexes, cette option ne s'adapte pas bien. Ainsi, vous pouvez obtenir la même chose avec ceci:

$ printf '%s\0' {a..c}{1..3} | sort -zk 1.2,1.2 | tr '\0' ' '

qui renvoie:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Semble un peu désordonné, mais maintenant, j'ai un énorme contrôle dans l'ordre, juste changer deux caractères dans la commande ci-dessus; par exemple:

$ echo {a..b}{1..2}{a..b}{1..2}

cela s'étendra à:

a1a1 a1a2 a1b1 a1b2 a2a1 a2a2 a2b1 a2b2 b1a1 b1a2 b1b1 b1b2 b2a1 b2a2 b2b1 b2b2

Supposons que je veuille tout 1dans la deuxième extension, puis 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.2 | tr '\0' ' '
a1a1 a1a2 a1b1 a1b2 b1a1 b1a2 b1b1 b1b2 a2a1 a2a2 a2b1 a2b2 b2a1 b2a2 b2b1 b2b2

Supposons que je veuille tout adans la troisième extension, puis b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.3,1.3 | tr '\0' ' '
a1a1 a1a2 a2a1 a2a2 b1a1 b1a2 b2a1 b2a2 a1b1 a1b2 a2b1 a2b2 b1b1 b1b2 b2b1 b2b2

Supposons que je veuille tout 1dans la quatrième extension, puis 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.4,1.4 | tr '\0' ' '
a1a1 a1b1 a2a1 a2b1 b1a1 b1b1 b2a1 b2b1 a1a2 a1b2 a2a2 a2b2 b1a2 b1b2 b2a2 b2b2

Supposons que je veuille tout 1aau milieu, alors 1b, alors 2a, puis 2b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.3 | tr '\0' ' '
a1a1 a1a2 b1a1 b1a2 a1b1 a1b2 b1b1 b1b2 a2a1 a2a2 b2a1 b2a2 a2b1 a2b2 b2b1 b2b2

Vous pouvez même, tout aussi facilement, inverser n'importe quel ordre dans les extensions ci-dessus, en ajoutant simplement un rà la commande précédente; par exemple, le dernier:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -rzk 1.2,1.3 | tr '\0' ' '
b2b2 b2b1 a2b2 a2b1 b2a2 b2a1 a2a2 a2a1 b1b2 b1b1 a1b2 a1b1 b1a2 b1a1 a1a2 a1a1

Note_1 : généralement, si cette extension finale doit être utilisée comme une liste d'arguments, l'espace de fin n'est pas un problème; mais si vous voulez vous en débarrasser, vous pouvez ajouter, par exemple, à n'importe laquelle des commandes ci-dessus| sed 's/ $//' ; ou même| sed 's/ $/\n/', pour changer cet espace de fuite pour unnewline

Note_2 : Dans les exemples ci-dessus, j'ai utilisé des sous-ensembles de deux éléments (c'est-à-dire: {a, b} et {1,2} ) juste pour plus de simplicité dans la preuve de concept: vous pouvez utiliser des sous-ensembles de n'importe quelle longueur finie, et le commande correspondante, serait comparable.

matsib.dev
la source
5

bash, ksh, zsh

Un liner qui fonctionne en (bash, ksh, zsh) (tous les shells ne peuvent pas faire "l'expansion de l'accolade" dans l'ordre inverse):

$ echo {3..1}{c..a} | rev
a1 b1 c1 a2 b2 c2 a3 b3 c3

Une alternative qui utilise eval(qui est toujours pour bash, ksh, zsh et peut être plus cryptique) est:

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

Pour comprendre ce qui se passe, remplacez evalpar echo:

$ echo echo '{a..c}'{1..3}
echo {a..c}1 {a..c}2 {a..c}3

La commande exécutée (après l'extension eval) l'est en fait echo {a..c}1 {a..c}2 {a..c}3. Qui s'agrandit à votre guise / besoin.

tous les coquillages

Il y a plusieurs coquilles sans "extensions d'accolade", donc, impossible de l'utiliser pour "toutes les coquilles". Nous avons besoin d'une boucle (avec un espace blanc à la fin):

$ for i in 1 2 3; do for j in a b c; do printf "%s%s " "$j" "$i"; done; done; echo
a1 b1 c1 a2 b2 c2 a3 b3 c3 

Si aucun espace de fin ne doit être ajouté:

s=""
for i in 1 2 3; do
    for j in a b c; do
        printf "%s%s%s" "$s" "$j" "$i"
        s=" "
    done
done
echo

Impressions

a1 b1 c1 a2 b2 c2 a3 b3 c3

SI vous devez le faire pour de nombreuses valeurs, nous devons utiliser quelque chose de similaire à l'extension d'accolade pour générer une liste de nombres $(seq 10). Et, comme seq ne peut pas générer une liste de lettres, nous devons convertir en ascii les nombres générés:

s=""
for i in $(seq 4); do
    for j in $(seq 5); do
        printf "%s\\$(printf %03o $((96+j)))%s" "$s" "$i"
        s=" "
    done
done
echo

impressions:

a1 b1 c1 d1 e1 a2 b2 c2 d2 e2 a3 b3 c3 d3 e3 a4 b4 c4 d4 e4
Isaac
la source
Vous pouvez également ajouter yash -o braceexpandà la liste.
Stéphane Chazelas
@ StéphaneChazelas Je ne suis pas sûr de devoir le faire. La commande yash -o braceexpand -c 'echo {3..1}{c..a}'s'imprime 3{c..a} 2{c..a} 1{c..a}sous linux. Pas une "extension complète".
Isaac
3
{a..c}1 {a..c}2 {a..c}3

Les extensions d'accolade {a..c}{1..3}sont étendues de gauche à droite, vous obtenez donc d'abord a{1..3} b{1..3} c{1..3}, puis les lettres sont combinées avec les chiffres dans a1 a2 a3 b1 b2 b3 c1 c2 c3. Pour obtenir la commande que vous souhaitez, vous devrez utiliser l'expression légèrement plus longue ci-dessus.

Kusalananda
la source
Si vous vouliez le faire pour une large gamme de "nombres", ce ne serait plus pratique.
RUBEN GONÇALO MOROUÇO
3
@ RUBENGONÇALOMOROUÇO Non, ce ne serait pas le cas, et si vous le faites pour une large gamme de nombres, je suggérerais d'utiliser une approche alternative, comme une double boucle. Cela fonctionnerait pour plusieurs milliers de combinaisons, tandis qu'une accolade élargit mon déclencheur "liste d'arguments trop longue" dans certains contextes.
Kusalananda
2

Utiliser une boucle:

for n in {1..3}; do printf '%s\n' {a..c}"$n"; done

Cela va parcourir votre première extension, puis étendre chaque personnage avec le second.

Si vous avez besoin de la sortie sur une seule ligne, vous pouvez supprimer \n:

for n in {1..3}; do printf '%s ' {a..c}"$n"; done

Cela ne vous donnera pas une nouvelle ligne de fin, mais si vous la passez à une commande ou une variable qui ne devrait pas être un problème.

Jesse_b
la source
1
Pourquoi tant de votes négatifs pour une solution en boucle?
Isaac
Duh, je dois avoir mal lu la question. Mis à jour
Jesse_b
2

Cela fonctionne pour votre cas simple et peut être étendu, mais cela deviendrait rapidement incontrôlable. Les cas plus complexes pour lesquels cela ne fonctionnerait pas sont faciles à construire.

Inversez l'ordre des extensions d'accolade, puis échangez les caractères:

echo {1..3}{a..c} | sed -E 's/(.)(.)( ?)/\2\1\3/g'
En pause jusqu'à nouvel ordre.
la source
@muru: Oups. Fixé. Merci.
pause jusqu'à nouvel ordre.
@Isaac: J'ai corrigé l'espace de fin.
pause jusqu'à nouvel ordre.
0

Une méthode simple serait d'utiliser le tri (le 1.2,1.2 signifie que vous prenez un caractère à la deuxième position et vous terminez au même endroit).

$ for i in {a..c}{1..3}; do echo $i; done|sort -n -k1.2,1.2
a1
b1
c1
a2
b2
c2
a3
b3
c3

Si vous les voulez sur une seule ligne, vous pouvez utiliser tr comme ceci:

$ for i in {a..c}{1..3}; do echo $i; done|sort -n -k1.2,1.2|tr '\n' ' '
a1 b1 c1 a2 b2 c2 a3 b3 c3
Hopping Bunny
la source
-2

Fait par la méthode ci-dessous

for i in {1..10}; do for j in {a..c}; do echo $j$i; done; done| perl -pne "s/\n/ /g"

sortie

a1 b1 c1 a2 b2 c2 a3 b3 c3 a4 b4 c4 a5 b5 c5 a6 b6 c6 a7 b7 c7 a8 b8 c8 a9 b9 c9 a10 b10 c10
Praveen Kumar BS
la source
pensez aussifor i in {1..10}; do for j in {a..c}; do printf '%s ' "$j$i"; done; done;echo
Jeff Schaller