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?
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:
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
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 1
na 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 a
na 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 1
na 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 1a
no 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 r
ao 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.
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 eval
por 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.
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
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.
{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.
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.
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'
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
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
for i in {1..10}; do for j in {a..c}; do printf '%s ' "$j$i"; done; done;echo
yash -o braceexpand
à lista.