Bash Script para repetir cada palavra em uma linha?

13

Eu tenho uma string como: dog cat bird whale

E eu quero receber dog dog cat cat bird bird whale whale

Todas as palavras estão na mesma linha. Alguma idéia?

    
por Cristian 06.03.2014 / 19:29

8 respostas

28

Adicionando à família de soluções :-).

duplicator.sh :

for i; do echo -n "$i $i "; done; echo

Crie executáveis e agora:

$ ./duplicator.sh dog cat bird whale
dog dog cat cat bird bird whale whale

Alternativamente, como uma função de casca, e. para ser reutilizável em um script:

duplicator() {
    for i; do echo -n "$i $i "; done; echo
}

que pode então ser executado diretamente onde definido como

duplicator dog cat bird whale
    
por 06.03.2014 / 20:05
21

Você pode usar sed :

sed -r 's/(\S+)/ /g' filename

Se você quiser salvar as alterações no arquivo no local, diga:

sed -i -r 's/(\S+)/ /g' filename

Você também pode usar perl :

perl -M5.10.0 -ne 'say join " ", map{$_, $_} split " ";' filename

(Adicione a opção -i para salvar as alterações no arquivo no local.)

Ou, como sugerido por terdon :

perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;' filename

Citações de perlvar :

@F

The array @F contains the fields of each line read in when autosplit mode is turned on. See perlrun for the -a switch. This array is package-specific, and must be declared or given a full package name if not in package main when running under strict 'vars'.

    
por 06.03.2014 / 19:34
4

O que seria isso sem uma resposta awk/gawk :

$ awk '{ for(i=1;i<=NF+1;i+=1/2) { printf("%s ",$i); }}' <<<"dog cat bird whale"
dog dog cat cat bird bird whale whale 

Se uma nova linha final é importante:

$ awk '{ for(i=1;i<=NF+1;i+=1/2) { printf("%s ",$i); }} END{print ""}' <<<"dog cat bird whale"
    
por 06.03.2014 / 20:02
3
s="dog cat bird wale"
ss=$( tr ' ' '\n' <<< "$s" | sed p | tr '\n' ' ' )
echo "$ss"
dog dog cat cat bird bird wale wale 
    
por 06.03.2014 / 19:45
1

Se você tiver sua string em uma variável, digamos foo="dog cat bird whale" , você poderia fazer:

  • Festa pura:

    $ echo "$foo" | (read a b c d && echo "$a $a $b $b $c $c $d $d")
    dog dog cat cat bird bird whale whale
    

    Explicação: Os parênteses são necessários para que read e echo ocorram na mesma subshell e possam, portanto, compartilhar variáveis. Sem eles, o echo apenas imprimia uma linha em branco.

  • coreutils:

    $ join -j 5 -o 1.1,1.1,1.2,1.2,1.3,1.3,1.4,1.4 <(echo $foo) <(echo)
    dog dog cat cat bird bird whale whale
    

    Explicação: O -o sinalizador de join permite que você defina o formato de saída. Aqui, estou dizendo para imprimir o 1º campo do 1º arquivo ( 1.1 ), seguido pelo 2º campo do 1º arquivo ( 1.2 ) etc. Dessa forma, cada campo do 1º arquivo é impresso duas vezes. No entanto, join foi projetado para, bem, unir duas linhas de entrada em um campo comum. Então, eu também passo uma linha em branco ( <(echo) ) e depois a ignoro. O -j define o campo de união, definindo-o como um que não existe (o quinto) faz com que join imprima a linha inteira.

    Se você não se importa com o espaço em branco ou com a ordem de entrada, pode fazer

    $ paste <(echo $foo) <(echo $foo)
    dog cat bird wale   dog cat bird wale
    
  • Perl 1:

    $ echo $foo | perl -lane 'push @k, $_,$_ for @F; print "@k"'
    dog dog cat cat bird bird whale whale
    

    Explicação:

    -l: adds a newline to each print call (among other things)
    -a: turns on field splitting, fields are saved as @F
    -n: process input line by line
    -e: give a script as a command line parameter.
    

    O script acima salvará cada campo (de @F ) duas vezes na matriz @k e, em seguida, imprimirá @k . Se você não precisa da nova linha, você pode simplificar para

    $ echo $foo | perl -ane 'print " $_ $_" for @F'
    
  • Perl 2:

    $ echo $foo | perl -0040 -pne 'print "$_"' | paste - - 
    dog dog cat cat bird bird whale whale
    

    Explicação: A opção -0 define o separador de registro de entrada (como um número hexadecimal ou octal, consulte aqui para conversões). Aqui, estou configurando para octal 040 , que é um espaço. O -p faz com que perl imprima cada "linha" de entrada e, como definimos o separador de registro como espaço, as linhas agora são definidas por espaços, então cada campo é impresso duas vezes.

  • awk :

    $ echo $foo | awk '{for(i=1;i<=NF;i++){$i=$i" "$i;} 1;}'
    dog dog cat cat bird bird whale whale
    

    Explicação: NF é o número de campos, por isso o script acima passa por cada campo e o acrescenta a si mesmo. Feito isso, imprimimos a linha ( 1; é apenas uma abreviação para impressão).

por 07.03.2014 / 03:55
0

Agora, para uma resposta python :

Na linha de comando:

$ python -c "import sys; s=sys.argv[1:]; print(' '.join(j for i in zip(s,s)for j in i));" dog cat bird whale

De stdin:

$ python -c "s=input().split(); print(' '.join(j for i in zip(s,s)for j in i));" <<<"dog cat bird whale"

O resultado em ambos os casos:

dog dog cat cat bird bird whale whale
    
por 07.03.2014 / 00:47
0

Ligeiramente over-the-top, mas uma resposta haskell :

$ ghc -e "getLine >>= putStrLn . unwords . (concatMap $ replicate 2) . words" <<<"dog cat bird whale"
dog dog cat cat bird bird whale whale
    
por 07.03.2014 / 19:59
0

Outra abordagem, também usando apenas bash builtins

$ string="dog cat bird whale"
$ twix() { while [[ ! -z $1 ]]; do printf "%s %s " $1 $1; shift; done; }
$ twix $string
dog dog cat cat bird bird whale whale

Eu não vejo nenhum benefício em comparação com a resposta principal, apenas para mostrar uma maneira ligeiramente diferente, que pode ser mais adequada para alguns propósitos.

    
por 11.03.2014 / 22:50