Bash Script pour répéter chaque mot d'une ligne?

13

J'ai une chaîne comme: dog cat bird whale

Et je veux avoir dog dog cat cat bird bird whale whale

Tous les mots sont sur la même ligne. Une idée?

Cristian
la source

Réponses:

28

Ajout à la famille de solutions :-).

duplicator.sh:

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

Rendez exécutable, et maintenant:

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

Alternativement comme fonction shell, par exemple pour être réutilisable dans un script:

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

qui peut ensuite être exécuté directement là où il est défini comme

duplicator dog cat bird whale
Daniel Andersson
la source
4
J'aime la simplicité de l'approche shell.
Henk Langeveld
Eh bien j'aime vraiment la simplicité de cette solution
Cristian
Bien mieux que les deux premières manières auxquelles j'ai pensé.
Joe
21

Vous pouvez utiliser sed:

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

Si vous souhaitez enregistrer les modifications apportées au fichier sur place, dites:

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

Vous pouvez également utiliser perl:

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

(Ajoutez l' -ioption pour enregistrer les modifications apportées au fichier sur place.)

Ou, comme l'a suggéré terdon :

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

Citant de perlvar:

@F

Le tableau @Fcontient les champs de chaque ligne lue lorsque le mode de séparation automatique est activé. Voir perlrun pour le -acommutateur. Ce tableau est spécifique au package et doit être déclaré ou donné un nom de package complet s'il n'est pas dans le package principal lors de l'exécution sous strict 'vars'.

devnull
la source
4
Shorter: sed -r 's/\S+/& &/g'.
cYrus
2
Ce n'est pas un script Bash. L'appel d'une commande (même à partir d'un shell Bash) ne constitue pas un script Bash.
Peter Mortensen
4
@PeterMortensen Vous êtes techniquement correct (le meilleur type de correct), mais pratiquement tous les systèmes sur lesquels bash est installé auront également les outils Unix standard, y compris sed et awk. L'intérêt du script shell (enfin, une grande partie du but) est de pointer les commandes au bon endroit.
evilsoup
3
@PeterMortensen C'est incorrect. Un script bash peut appeler des commandes externes. Un script bash doit commencer par une ligne shebang, mais ce n'est pas strictement nécessaire. La question ne précisait pas que le script bash ne devait pas appeler de commandes externes (souvent appelé «script bash pur»).
Gilles 'SO- arrête d'être méchant'
2
Belle astuce perl. Vous pouvez le raccourcir avec -a:perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;'
terdon
4

Que serait-ce sans awk/gawkréponse:

$ 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 une nouvelle ligne de terminaison est importante:

$ awk '{ for(i=1;i<=NF+1;i+=1/2) { printf("%s ",$i); }} END{print ""}' <<<"dog cat bird whale"
user19087
la source
Mes premières déprécient. Juste curieux, pourquoi? Y a-t-il un problème avec le script? Ou une version plus abrégée?
user19087
Pas mon downvote, mais for(i=1;i<=NF;++i) printf "%s %s ",$i,$i;est à la fois plus court et plus lisible, à mon humble avis .
rici
Difficile de contester cela, j'ai donc simplifié et raccourci ma réponse (profite désormais des indices arrondis aux pouces) sans changer l'approche. Espérons que ce soit maintenant plus clair.
user19087
1
Ce n'est pas un script Bash. L'appel d'une commande (même à partir d'un shell Bash) ne constitue pas un script Bash.
Peter Mortensen
Mettre cela dans un script est cependant 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 
glenn jackman
la source
1
J'ai pensé à écrire sed -n 'p;p'- je pensais que c'était plus transparent sur ce qu'il faisait.
glenn jackman
1
Vous devriez ajouter cela à la réponse!
terdon
1

Si vous avez votre chaîne dans une variable, disons foo="dog cat bird whale", vous pouvez faire:

  • Bash pur:

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

    Explication: Les parenthèses sont nécessaires pour que le readet echose produisent dans le même sous-shell et peuvent donc partager des variables. Sans eux, le echoserait simplement imprimer une ligne vierge.

  • 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

    Explication: Le -odrapeau de joinvous permet de définir le format de sortie. Ici, je lui dis d'imprimer le 1er champ du 1er fichier ( 1.1), suivi du 2ème champ du 1er fichier ( 1.2) etc. De cette façon, chaque champ du 1er fichier est imprimé deux fois. Cependant, joinest conçu pour, bien, joindre deux lignes d'entrée sur un champ commun. Donc, je lui passe également une ligne vide ( <(echo)), puis je l'ignore. Le -jdéfinit le champ de jointure, en le définissant sur un champ qui n'existe pas (le 5e) provoque l' joinimpression de la ligne entière.

    Si vous ne vous souciez pas des espaces ou de l'ordre d'entrée, vous pouvez le faire

    $ 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

    Explication:

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

    Le script ci-dessus enregistre chaque champ (de @F) deux fois dans le tableau @k, puis l'imprime @k. Si vous n'avez pas besoin de la nouvelle ligne de fin, vous pouvez simplifier

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

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

    Explication: L' -0option définit le séparateur d'enregistrement d'entrée (sous forme de nombre hexadécimal ou octal, voir ici pour les conversions). Ici, je le mets à octal 040qui est un espace. Les -pmarques perlimpriment chaque "ligne" d'entrée et puisque nous avons défini le séparateur d'enregistrement sur espace, les lignes sont maintenant définies par des espaces, donc chaque champ est imprimé deux fois.

  • awk:

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

    Explication: NF est le nombre de champs, donc le script ci-dessus passe par chaque champ et l'ajoute à lui-même. Une fois cela fait, nous imprimons la ligne ( 1;c'est juste un raccourci pour l'impression).

terdon
la source
0

Maintenant pour une pythonréponse:

Depuis la ligne de commande:

$ 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

Depuis stdin:

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

Le résultat dans les deux cas:

dog dog cat cat bird bird whale whale
user19087
la source
0

Un peu exagéré, mais une haskellréponse:

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

Une autre approche, utilisant également uniquement les builds bash

$ 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

Je ne vois aucun avantage par rapport à la première réponse, juste pour montrer une manière légèrement différente, qui pourrait être mieux adaptée à certaines fins.

mpy
la source
echoest également un shell intégré à Bash (test type echo).
Daniel Andersson
@DanielAndersson: Bien sûr, c'est la raison pour laquelle j'ai écrit « aussi en utilisant seulement builtins bash », d' avoir votre belle (déjà attribué +1) réponse à l' esprit.
mpy
OK, vous pouvez classer mon commentaire comme confusion linguistique :-).
Daniel Andersson