Bash Script para repetir cada palabra en una línea?


13

Tengo una cadena como: dog cat bird whale

Y quiero conseguir dog dog cat cat bird bird whale whale

Todas las palabras están en la misma línea. ¿Alguna idea?

Respuestas:


28

Agregando a la familia de soluciones :-).

duplicator.sh:

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

Hacer ejecutable, y ahora:

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

Alternativamente como una función de shell, por ejemplo, para ser reutilizable dentro de un script

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

que luego se puede ejecutar directamente donde se define como

duplicator dog cat bird whale

44
Me gusta la simplicidad del enfoque de shell.
Henk Langeveld

Bueno, realmente amo la simplicidad de esta solución
Cristian

Mucho mejor que las dos primeras formas en que pensé.
Joe

21

Podrías usar sed:

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

Si desea guardar los cambios en el archivo en el lugar, diga:

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

También puedes usar perl:

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

(Agregue la -iopción para guardar los cambios en el archivo en el lugar).

O, como lo sugiere terdon :

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

Citando de perlvar:

@F

La matriz @Fcontiene los campos de cada línea leída cuando el modo de división automática está activado. Ver perlrun para el -acambio. Esta matriz es específica del paquete y debe declararse o recibir un nombre de paquete completo si no está en el paquete principal cuando se ejecuta bajo strict 'vars'.


44
Más corto: sed -r 's/\S+/& &/g'.
cYrus

2
Ese no es un script Bash. Invocar algún comando (incluso desde un shell Bash) no consiste en un script Bash.
Peter Mortensen

44
@PeterMortensen Eres técnicamente correcto (el mejor tipo de correcto), pero prácticamente todos los sistemas que tienen instalado bash también tendrán las herramientas estándar de Unix, incluidos sed y awk. El objetivo de las secuencias de comandos de shell (bueno, una gran parte de ellas) es apuntar los comandos al lugar correcto.
evilsoup

3
@PeterMortensen Esto es incorrecto. Un script bash puede llamar a comandos externos. Un script bash debe comenzar con una línea shebang, pero esto no es estrictamente necesario. La pregunta no especificaba que el script bash no debería llamar a comandos externos (a menudo llamado "script bash puro").
Gilles 'SO- deja de ser malvado'

2
Buen truco perl. Puede hacerlo más corto con -a:perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;'
terdon

4

¿Qué sería esto sin una awk/gawkrespuesta?

$ 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 

Si una nueva línea de terminación es importante:

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

Mi primer voto negativo. Solo curiosidad, ¿por qué? ¿Hay algo mal con el guión? ¿O una versión más abreviada?
user19087

No es mi voto negativo, pero for(i=1;i<=NF;++i) printf "%s %s ",$i,$i;es más corto y más legible, en mi humilde opinión.
rici

Es difícil discutir eso, así que simplifiqué y acorté mi respuesta (ahora aprovecha los índices que se redondean a ints) sin cambiar el enfoque. Esperemos que ahora esté más claro.
user19087

1
Ese no es un script Bash. Invocar algún comando (incluso desde un shell Bash) no consiste en un script Bash.
Peter Mortensen

Sin embargo, poner esto en un script es trivial.
Kevin

3
s="dog cat bird wale"
ss=$( tr ' ' '\n' <<< "$s" | sed p | tr '\n' ' ' )
echo "$ss"
dog dog cat cat bird bird wale wale 

1
Pensé en escribir sed -n 'p;p', pensé que era más transparente sobre lo que estaba haciendo.
Glenn Jackman

1
¡Deberías agregar eso a la respuesta!
terdon

1

Si tiene su cadena en una variable, por ejemplo foo="dog cat bird whale", podría hacer:

  • Golpe puro:

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

    Explicación: Los paréntesis son necesarios para que ready echosuceda en la misma subshell y, por lo tanto, puedan compartir variables. Sin ellos, echosimplemente imprimirían una línea en blanco.

  • 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

    Explicación: El -oindicador de le joinpermite establecer el formato de salida. Aquí, le digo que imprima el primer campo del primer archivo ( 1.1), seguido del segundo campo del primer archivo ( 1.2), etc. De esta manera, cada campo del primer archivo se imprime dos veces. Sin embargo, joinestá diseñado para, bueno, unir dos líneas de entrada en un campo común. Entonces, también le paso una línea en blanco (<(echo) ) y luego lo ignoro. Los -jconjuntos se unen las de campo, estableciendo que a uno que no existe (la quinta) hace que joinimprimir toda la línea.

    Si no le importa el espacio en blanco o el orden de entrada, puede hacer

    $ 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

    Explicación:

    -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.

    La secuencia de comandos anterior guardará cada campo (de @F) dos veces en la matriz @ky luego imprimirá @k. Si no necesita la nueva línea final, puede simplificar

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

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

    Explicación: La -0opción establece el separador de registro de entrada (como un número hexadecimal u octal, consulte aquí las conversiones). Aquí, lo estoy configurando en octal, 040que es un espacio. Las -pmarcas perlimprimen cada "línea" de entrada y dado que hemos establecido el separador de registros en espacio, las líneas ahora están definidas por espacios, por lo que cada campo se imprime dos veces.

  • awk:

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

    Explicación: NF es el número de campos, por lo que el script anterior recorre cada campo y lo agrega a sí mismo. Una vez hecho esto, imprimimos la línea ( 1;es solo una abreviatura para imprimir).


0

Ahora por un python respuesta:

Desde la línea 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"

El resultado en ambos casos:

dog dog cat cat bird bird whale whale

0

Ligeramente exagerado, pero una haskellrespuesta:

$ ghc -e "getLine >>= putStrLn . unwords . (concatMap $ replicate 2) . words" <<<"dog cat bird whale"
dog dog cat cat bird bird whale whale

0

Otro enfoque, que también usa solo 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

No veo ningún beneficio en comparación con la respuesta principal, solo para mostrar una forma ligeramente diferente, que podría ser más adecuada para algunos propósitos.


echoTambién es un shell incorporado en Bash (prueba type echo).
Daniel Andersson

@DanielAndersson: Claro, por eso escribí " también usando solo bash builtins", teniendo en mente su buena respuesta (ya con +1).
mpy

OK, puedes clasificar mi comentario como confusión de idioma :-).
Daniel Andersson
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.