Aplicar expansión de llaves en "orden inverso"


21

Por ejemplo se {a..c}{1..3}expande a a1 a2 a3 b1 b2 b3 c1 c2 c3.

Si quisiera imprimir a1 b1 c1 a2 b2 c2 a3 b3 c3, ¿hay alguna forma análoga de hacerlo? ¿Cuál es la forma más simple?

Respuestas:


30

Podrías hacerlo:

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

Que luego le dice al shell que evalúe:

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

10

Para este caso en particular, creo que la opción dada por Stéphane Chazelas es la mejor.

Por otro lado, cuando expande cosas más complejas, esta opción no escala bien. Entonces, puedes lograr lo mismo con esto:

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

que devuelve:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Parece un poco desordenado, pero ahora, tengo un gran control en el orden, solo cambiando dos caracteres en el comando anterior; por ejemplo:

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

esto se expandirá a:

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

Supongamos que quiero todo 1en la segunda expansión, luego el 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

Supongamos que quiero todo aen la tercera expansión, luego el 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

Supongamos que quiero todo 1en la cuarta expansión, luego el 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

Supongamos que quiero todo 1aen el medio, luego 1b, luego 2a, luego 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

Incluso puede, con la misma facilidad, invertir cualquier orden en las expansiones anteriores, simplemente agregando un rcomando anterior; por ejemplo, el último:

$ 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

Nota_1 : generalmente, si esta expansión final se va a utilizar como una lista de argumentos, el espacio final no es un problema; pero si desea deshacerse de él, puede agregarlo a cualquiera de los comandos anteriores, por ejemplo| sed 's/ $//' ; o incluso| sed 's/ $/\n/', cambiar ese espacio final por unnewline

Nota_2 : En los ejemplos anteriores, he usado subconjuntos de dos elementos (es decir, {a, b} y {1,2} ) solo por simplicidad en la prueba de concepto: puede usar subconjuntos de cualquier longitud finita, y comando correspondiente, sería comparable.


5

bash, ksh, zsh

Una línea que funciona en (bash, ksh, zsh) (no todos los shells pueden hacer "Expansión de llaves" en orden inverso):

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

Una alternativa que usa eval(que todavía es para bash, ksh, zsh y puede ser más críptico) es:

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

Para entender lo que sucede, reemplácelo evalcon echo:

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

El comando ejecutado (después de la expansión eval) es en realidad echo {a..c}1 {a..c}2 {a..c}3. Que se expande como quieras / necesites.

todas las conchas

Hay varios proyectiles sin "expansiones de llaves", por lo que no es posible usar eso para "todos los proyectiles". Necesitamos un bucle (con un espacio en blanco al final):

$ 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 no debe agregar espacio final:

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

Huellas dactilares

a1 b1 c1 a2 b2 c2 a3 b3 c3

SI necesita hacer esto para muchos valores, necesitamos usar algo similar a la expansión de llaves para generar una lista de números $(seq 10). Y, dado que seq no puede generar una lista de letras, necesitamos convertir a ascii los números generados:

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

huellas dactilares:

a1 b1 c1 d1 e1 a2 b2 c2 d2 e2 a3 b3 c3 d3 e3 a4 b4 c4 d4 e4

También puede agregar yash -o braceexpanda la lista.
Stéphane Chazelas

@ StéphaneChazelas No estoy seguro de que deba hacerlo. El comando se yash -o braceexpand -c 'echo {3..1}{c..a}'imprime 3{c..a} 2{c..a} 1{c..a}en Linux. No es una "expansión de llaves" completa.
Isaac

3
{a..c}1 {a..c}2 {a..c}3

Las expansiones de llaves {a..c}{1..3}se expanden de izquierda a derecha, por lo que primero obtienes a{1..3} b{1..3} c{1..3}y luego las letras se combinan con los números en a1 a2 a3 b1 b2 b3 c1 c2 c3. Para obtener el orden que desea, deberá usar la expresión un poco más larga de arriba.


Si quisieras hacerlo para una amplia gama de "números" ya no sería práctico.
RUBEN GONÇALO MOROUÇO

3
@ RUBENGONÇALOMOROUÇO No, no lo haría, y si lo hace para una amplia gama de números, sugeriría utilizar un enfoque alternativo, como un doble bucle. Eso funcionaría para muchos miles de combinaciones, mientras que un paréntesis expansivo desencadena mi "lista de argumentos demasiado larga" en ciertos contextos.
Kusalananda

2

Usando un bucle:

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

Esto recorrerá su primera expansión y luego expandirá cada personaje con la segunda.

Si necesita la salida en una sola línea, puede eliminar \n:

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

Esto no le dará una nueva línea final, pero si la pasa a un comando o variable, eso no debería ser un problema.


1
¿Por qué tantos votos negativos para una solución de bucle?
Isaac

Duh debo haber leído mal la pregunta. Actualizado
Jesse_b

2

Esto funciona para su caso simple y se puede extender, pero rápidamente se saldría de control. Los casos más complejos para los que esto no funcionaría son fáciles de construir.

Invierta el orden de las expansiones de llaves, luego intercambie los caracteres:

echo {1..3}{a..c} | sed -E 's/(.)(.)( ?)/\2\1\3/g'

@muru: ¡Vaya! Fijo. Gracias.
Pausado hasta nuevo aviso.

@ Isaac: arreglé el espacio final.
Pausado hasta nuevo aviso.

0

Un método simple sería usar sort (el 1.2,1.2 significa que toma un personaje en la segunda posición y termina en el mismo lugar).

$ 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 los quiere en una línea, puede usar tr así:

$ 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

-2

Hecho por el siguiente método

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

salida

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

considere tambiénfor i in {1..10}; do for j in {a..c}; do printf '%s ' "$j$i"; done; done;echo
Jeff Schaller
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.