Aplique expansão de braçadeira na "ordem inversa"


21

Por exemplo, {a..c}{1..3}expande para a1 a2 a3 b1 b2 b3 c1 c2 c3.

Se eu quisesse imprimir a1 b1 c1 a2 b2 c2 a3 b3 c3, existe uma maneira análoga de fazer isso? Qual é a maneira mais simples?

Respostas:


30

Você poderia fazer:

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

O que então diz ao shell para avaliar:

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

10

Para este caso em particular, acho que a opção dada por Stéphane Chazelas é a melhor.

Por outro lado, quando você expande coisas mais complexas, essa opção não é bem dimensionada. Então, você pode conseguir o mesmo com isso:

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

que retorna:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Parece um pouco confuso, mas agora, eu tenho um controle enorme na ordem, apenas alterando dois caracteres no comando acima; por exemplo:

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

isso será expandido para:

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

Suponha que eu queira tudo 1na segunda expansão e, em seguida, o 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

Suponha que eu queira tudo ana terceira expansão e, em seguida, o 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

Suponha que eu queira tudo 1na quarta expansão, depois o 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

Suponha que eu queira tudo 1ano meio, então 1b, então 2a, então 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

Você pode, com a mesma facilidade, reverter qualquer ordem nas expansões acima, apenas adicionando um rao comando anterior; por exemplo, o ú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 : geralmente, se essa expansão final for usada como uma lista de argumentos, o espaço à direita não será um problema; mas se você quiser se livrar dele, pode adicionar a qualquer um dos comandos acima, por exemplo| sed 's/ $//' ; ou até mesmo| sed 's/ $/\n/', para alterar esse espaço à direitanewline

Nota_2 : Nos exemplos acima, usei subconjuntos de dois elementos (por exemplo: {a, b} e {1,2} ) apenas para simplificar a prova de conceito: você pode usar subconjuntos de qualquer comprimento finito e o comando correspondente, seria comparável.


5

bash, ksh, zsh

Um liner único que funciona em (bash, ksh, zsh) (nem todos os shells podem fazer "Brace expansion" na ordem inversa):

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

Uma alternativa usada eval(que ainda é para bash, ksh, zsh e pode ser mais enigmática) é:

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

Para entender o que acontece, substitua evalpor echo:

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

O comando executado (após a expansão de avaliação) é realmente echo {a..c}1 {a..c}2 {a..c}3. O que se expande conforme você deseja / precisa.

todas as conchas

Existem vários shells sem "expansões de chaves", portanto, não é possível usá-los para "todos os shells". Precisamos de um loop (com um espaço em branco à direita):

$ 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 

Se você não deve adicionar espaço à direita:

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

Impressões

a1 b1 c1 a2 b2 c2 a3 b3 c3

Se você precisar fazer isso para muitos valores, precisamos usar algo semelhante à expansão de chaves para gerar uma lista de números $(seq 10). E, como o seq não pode gerar uma lista de letras, precisamos converter para ascii os números gerados:

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

impressões:

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

Você também pode adicionar yash -o braceexpandà lista.
Stéphane Chazelas

@ StéphaneChazelas Não tenho certeza se deveria. O comando yash -o braceexpand -c 'echo {3..1}{c..a}'imprime 3{c..a} 2{c..a} 1{c..a}no linux. Não é uma "expansão de chave" completa.
Isaac

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

As expansões de chaves {a..c}{1..3}são expandidas da esquerda para a direita, para que você obtenha primeiro a{1..3} b{1..3} c{1..3}e, em seguida, as letras sejam combinadas com os números em a1 a2 a3 b1 b2 b3 c1 c2 c3. Para obter o pedido desejado, você terá que usar a expressão um pouco mais longa acima.


Se você quisesse fazer isso por uma grande variedade de "números", não seria mais prático.
RUBEN GONÇALO MOROUÇO 19/06

3
@ RUBENGONÇALOMOROUÇO Não, não, e se você estiver fazendo isso por uma grande variedade de números, sugiro usar uma abordagem alternativa, como um loop duplo. Isso funcionaria para muitos milhares de combinações, enquanto uma chave amplia meu gatilho "lista de argumentos muito longa" em certos contextos.
Kusalananda

2

Usando um loop:

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

Isso percorrerá sua primeira expansão e depois expandirá cada caractere com a segunda.

Se você precisar da saída toda em uma linha, poderá remover o \n:

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

Isso não fornecerá uma nova linha à direita, mas se você a estiver passando para um comando ou variável que não deve ser um problema.


1
Por que tantos votos negativos em uma solução de loop?
Isaac

Duh, eu devo ter lido a pergunta errado. Atualizado
Jesse_b

2

Isso funciona para o seu caso simples e pode ser estendido, mas rapidamente sairia do controle. Casos mais complexos para os quais isso não funcionaria são fáceis de construir.

Inverta a ordem das expansões de chaves e troque os caracteres:

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

@muru: Opa. Fixo. Obrigado.
Pausado até novo aviso.

@ Isaac: Corrigi o espaço à direita.
Pausado até novo aviso.

0

Um método simples seria usar a classificação (o 1.2,1.2 significa que você pega um caractere na segunda posição e termina no mesmo 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

Se você os quiser em uma linha, use tr da seguinte maneira:

$ 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

Feito pelo método abaixo

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

saída

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

veja tambémfor i in {1..10}; do for j in {a..c}; do printf '%s ' "$j$i"; done; done;echo
Jeff Schaller
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.