Em `sed`, como posso colocar um" & "entre os caracteres em uma string?

11

Pode sedfazer algo como:

12345

tornar-se :

1&2&3&4&5

?

GAD3R
fonte

Respostas:

25

Com o GNU sed:

sed 's/./\&&/2g'

( ssubstitua cada gcaractere ( .) pelo mesmo ( &) precedido por &( \&), mas apenas começando na segunda ocorrência ( 2)).

Portably:

sed 's/./\&&/g;s/&//'

(substitua todas as ocorrências, mas remova a primeira &que não queremos).

Com algumas awkimplementações (não POSIX, pois o comportamento não é especificado para um FS vazio):

awk -F '' -v OFS="&" '{$1=$1;print}'

(com gawke algumas outras awkimplementações, um separador de campo vazio divide os registros em seus constituintes de caracteres . O separador de campo de saída ( OFS) está definido como &. Atribuímos um valor a $1(ele mesmo) para forçar o registro a ser regenerado com o novo separador de campo antes de imprimi-lo, NF=NFtambém funciona e é um pouco mais eficiente em muitas implementações do awk, mas o comportamento quando você faz isso não é atualmente especificado pelo POSIX).

perl:

perl -F -lape '$_=join"&",@F' 

( -peexecuta o código para cada linha e imprime o resultado ( $_); -lremove e adiciona automaticamente as terminações de linha; -apreenche @Fcom a divisão de entrada no delimitador definido -F, que é uma cadeia vazia. O resultado é dividir cada caractere @F, junte-os com '&' e imprima a linha.)

Alternativamente:

perl -pe 's/(?<=.)./&$&/g' 

(substitua cada caractere, desde que seja precedido por outro caractere (veja o operador regexp (? <= ...))

Usando zshoperadores shell:

in=12345
out=${(j:&:)${(s::)in}}

(novamente, divida em um separador de campo vazio usando o s::sinalizador de expansão de parâmetro e junte-se a &)

Ou:

out=${in///&} out=${out#?}

(substitua todas as ocorrências de nada (portanto, antes de cada caractere) pelo &uso do ${var//pattern/replacement}operador ksh (embora em kshum padrão vazio signifique outra coisa, e ainda outra coisa, não sei ao certo em que bash), e remova a primeira com a ${var#pattern}remoção do POSIX operador).

Usando ksh93operadores shell:

in=12345
out=${in//~(P:.(?=.))/\0&}

( ~(P:perl-like-RE)sendo um operador glob do ksh93 para usar expressões regulares do tipo perl (diferente do perl ou do PCRE), (?=.)sendo o operador antecipado: substitua um caractere, desde que seja seguido por outro caractere ( \0) e &)

Ou:

out=${in//?/&\0}; out=${out#?}

(substitua cada caractere ( ?) por &e ele próprio ( \0) e removemos o caractere supérfluo)

Usando bashoperadores shell:

shopt -s extglob
in=12345
out=${in//@()/&}; out=${out#?}

(o mesmo que zsh's, exceto que você precisa @()lá (um operador de ksh glob para a qual você precisa extglobem bash)).

Stéphane Chazelas
fonte
2
@AFSHIN, que não iria trabalhar em uma 012345entrada
Stéphane Chazelas
1
isso deve funcionarawk -F '' -v OFS="&" 'NF=NF'
αғsнιη
1
@AFSHIN, mas remova as linhas vazias. De maneira mais geral, ao usar uma ação como condição e pretender que o resultado da ação seja impresso, é necessário garantir que o valor retornado pela ação não seja uma sequência vazia ou uma sequência numérica que resolva para 0.
Stéphane Chazelas
1
Você poderia adicionar uma explicação rápida de como cada uma delas funciona? Parece que há algumas coisas incríveis para aprender aqui, mas eu nem sei onde começaria a pesquisar a maioria delas para ver como aplicá-las fora do escopo desse problema específico.
IMSoP 25/08
1
@ StéphaneChazelas Brilhante, obrigado. Pesquisando documentos complexos por coisas como sed é um pouco de arte, portanto, ter alguns exemplos práticos é uma ótima maneira de aprender novos bits que você nunca tinha visto antes.
IMSoP 25/08
15

Utilitários Unix:

fold -w1|paste -sd\& -

Explicado:

"fold -w1" - envolverá cada caractere de entrada em sua própria linha

dobra - enrole cada linha de entrada para caber na largura especificada

-w, --width = WIDTH usa colunas WIDTH em vez de 80

%echo 12345|fold -w1
1
2
3
4
5

"paste -sd\& -"- fundirá as linhas de entrada, usando &como separador

colar - mesclar linhas de arquivos

-s, --serial, cole um arquivo por vez, em vez de em paralelo

-d, --delimiters = LIST reutiliza caracteres de LIST em vez de TABs

%fold -w1|paste -sd\& -
1&2&3&4&5

(Observe que, se a entrada contiver várias linhas, elas serão unidas &)

zepelim
fonte
2
Falha nos caracteres multibyte. Tenteecho "abcdeéèfg" | fold -1 | paste -sd\& -
Isaac
3
@ Arrow Muito provavelmente você está apenas usando uma versão de bug do coreutils do fold , que não possui suporte completo a Unicode. O BSD fold, as versões corrigidas do RedHat do coreutils (por exemplo, Fedora ou CentOS), bem como a implementação do BusyBox, podem lidar com o Unicode de maneira agradável.
Zeppelin
5
A questão é especificamente sobre sed.
Alexander
6
@ Alexander - isso é verdade, e há várias boas sedrespostas disponíveis abaixo. E não vejo mal em demonstrar como a tarefa pode ser resolvida por outros meios.
Zeppelin
@ StéphaneChazelas> POSIXly, você precisaria fold -w 1 True, eu adicionei "-w", thx! "-", Por sua vez, não é necessário If no file operands are specified, the standard input shall be used
zeppelin
11

Usar sed

sed 's/./&\&/g;s/.$//'
αғsнιη
fonte
9
sed 's/\B/\&/g'

\ B - Corresponde a todos os lugares, exceto no limite de uma palavra; ou seja, corresponde se o caractere à esquerda e o caractere à direita são caracteres de "palavra" ou caracteres de "não palavra".

Informações: GNU sed manual, extensões de expressão regular .

Teste:

sed 's/\B/\&/g' <<< '12345'
1&2&3&4&5
MiniMax
fonte
5
Ideia interessante, mas a pergunta não diz que a string não contém um espaço, um ponto ou qualquer coisa que possa constituir um limite de palavra. Apenas diz "entre caracteres", que deve ser interpretado como "qualquer caractere".
xhienne
4

Isso será um pouco mais lento que algumas das outras respostas, mas é bem claro:

echo 12345 | perl -lnE 'say join "&", split //'
Glenn Jackman
fonte
4

Aqui está outra maneira. A primeira parte da expressão sed captura todos os caracteres e os substitui pelo personagem e um e comercial. A segunda parte remove o e comercial do final da linha.

echo 12345 | sed -r 's/(.)/\1\&/g;s/\&$//g'
1&2&3&4&5

Também funciona em caracteres multibyte.

Alexander
fonte
1
Não há necessidade de chamar sedduas vezes, um sedscript pode ter vários comandos:sed -r 's/(.)/\1\&/g; s/\&$//g'
xhienne
xhienne, obrigado, TIL! Atualizado a resposta.
Alexander Alexander