Bash Script, um jedes Wort in einer Zeile zu wiederholen?

13

Ich habe eine Zeichenfolge wie: dog cat bird whale

Und ich möchte bekommen dog dog cat cat bird bird whale whale

Alle Wörter befinden sich in derselben Zeile. Irgendeine Idee?

Cristian
quelle

Antworten:

28

Erweiterung der Lösungsfamilie :-).

duplicator.sh:

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

Ausführbar machen und jetzt:

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

Alternativ als Shell-Funktion, um zB innerhalb eines Skripts wiederverwendbar zu sein:

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

die dann direkt ausgeführt werden können, wo definiert als

duplicator dog cat bird whale
Daniel Andersson
quelle
4
Ich mag die Einfachheit des Shell-Ansatzes.
Henk Langeveld
Nun, ich liebe die Einfachheit dieser Lösung wirklich
Cristian
Viel besser als die ersten paar Möglichkeiten, die ich mir vorgestellt habe.
Joe
21

Sie könnten verwenden sed:

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

Wenn Sie die Änderungen an der Datei direkt speichern möchten, sagen Sie:

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

Sie könnten auch verwenden perl:

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

(Fügen Sie die -iOption hinzu, um die Änderungen an der Datei direkt zu speichern.)

Oder, wie von Terdon vorgeschlagen :

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

Zitat aus perlvar:

@F

Das Array @Fenthält die Felder jeder eingelesenen Zeile, wenn der Autosplit-Modus aktiviert ist. Siehe perlrun für den -aSchalter. Dieses Array ist paketspezifisch und muss deklariert oder mit einem vollständigen Paketnamen versehen werden, wenn es nicht unter package main ausgeführt wird strict 'vars'.

devnull
quelle
4
Kürzere: sed -r 's/\S+/& &/g'.
CYRUS
2
Das ist kein Bash-Skript. Das Aufrufen eines Befehls (auch aus einer Bash-Shell) besteht nicht aus einem Bash-Skript.
Peter Mortensen
4
@PeterMortensen Sie sind technisch korrekt (die beste Art von korrekt), aber praktisch jedes System, auf dem bash installiert ist, verfügt auch über die Standard-Unix-Tools, einschließlich sed und awk. Der springende Punkt beim Erstellen von Shell-Skripten (also ein großer Teil davon) ist, Befehle an die richtige Stelle zu platzieren.
Übelsuppe
3
@PeterMortensen Das ist falsch. Ein Bash-Skript kann externe Befehle aufrufen. Ein Bash-Skript sollte mit einer Shebang-Zeile beginnen, dies ist jedoch nicht unbedingt erforderlich. In der Frage wurde nicht angegeben, dass das Bash-Skript keine externen Befehle aufrufen soll (häufig als "reines Bash-Skript" bezeichnet).
Gilles 'SO - hör auf böse zu sein'
2
Netter Perl-Trick. Sie können es kürzer machen mit -a:perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;'
terdon
4

Was wäre das ohne eine awk/gawkAntwort:

$ 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 

Wenn ein abschließender Zeilenumbruch wichtig ist:

$ awk '{ for(i=1;i<=NF+1;i+=1/2) { printf("%s ",$i); }} END{print ""}' <<<"dog cat bird whale"
user19087
quelle
Meine erste Gegenstimme. Nur neugierig, warum? Stimmt etwas mit dem Skript nicht? Oder eine gekürzte Version?
user19087
Nicht meine Ablehnung, aber for(i=1;i<=NF;++i) printf "%s %s ",$i,$i;ist sowohl kürzer als auch lesbarer, IMHO.
rici
Das ist schwer zu argumentieren, daher habe ich meine Antwort vereinfacht und verkürzt (nutzt jetzt die Vorteile von Indizes, die auf Ints abgerundet werden), ohne den Ansatz zu ändern. Hoffentlich ist es jetzt klarer.
user19087
1
Das ist kein Bash-Skript. Das Aufrufen eines Befehls (auch aus einer Bash-Shell) besteht nicht aus einem Bash-Skript.
Peter Mortensen
Das in ein Skript einzufügen ist jedoch 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
quelle
1
Ich dachte über das Schreiben nach sed -n 'p;p'- ich dachte, dass das transparenter darüber ist, was es tut.
Glenn Jackman
1
Sie sollten das zur Antwort hinzufügen!
Terdon
1

Wenn Sie Ihre Zeichenfolge in einer Variablen haben, foo="dog cat bird whale"können Sie beispielsweise Folgendes tun:

  • Pure Bash:

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

    Erläuterung: Die Klammern werden benötigt, damit die readund echoin derselben Unterschale vorkommen und daher Variablen gemeinsam nutzen können. Ohne sie echowürde das nur eine leere Zeile ausgeben.

  • 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

    Erläuterung: Mit dem -oFlag von joinkönnen Sie das Ausgabeformat festlegen. Hier sage ich, dass das erste Feld der ersten Datei ( 1.1) gedruckt werden soll , gefolgt vom zweiten Feld der ersten Datei ( 1.2) usw. Auf diese Weise wird jedes Feld der ersten Datei zweimal gedruckt. Es wurde jedoch joinentwickelt, um zwei Eingabezeilen in einem gemeinsamen Feld zu verbinden. Also übergebe ich ihm auch eine Leerzeile ( <(echo)) und ignoriere sie dann. Die -jSätze kommen die Feld, es zu einer Einstellung , die nicht (5.) existiert bewirkt , dass joindie gesamte Zeile drucken.

    Wenn Sie sich nicht für Leerzeichen oder die Eingabereihenfolge interessieren, können Sie dies tun

    $ 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

    Erläuterung:

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

    Das obige Skript speichert jedes Feld (von @F) zweimal im Array @kund druckt es dann aus @k. Wenn Sie die nachgestellte Zeile nicht benötigen, können Sie sie vereinfachen

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

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

    Erläuterung: Die -0Option stellt die Eingabe voneinander zu trennen sind (als hexadezimale oder Oktalzahl, siehe hier für Umtausche). Hier setze ich es auf oktal, 040was ein Leerzeichen ist. Das -pbewirkt perl, dass jede Eingabe "zeilenweise" gedruckt wird, und da wir das Datensatztrennzeichen auf Leerzeichen gesetzt haben, werden Zeilen jetzt durch Leerzeichen definiert, sodass jedes Feld zweimal gedruckt wird.

  • awk:

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

    Erläuterung: NF Gibt die Anzahl der Felder an, sodass das obige Skript jedes Feld durchläuft und an sich selbst anfügt. Sobald dies erledigt ist, drucken wir die Zeile ( 1;ist nur eine Abkürzung für print).

terdon
quelle
0

Nun zu einer pythonAntwort:

Von der Kommandozeile:

$ 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

Von stdin:

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

Das Ergebnis in beiden Fällen:

dog dog cat cat bird bird whale whale
user19087
quelle
0

Etwas übertrieben, aber eine haskellAntwort:

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

Ein anderer Ansatz, bei dem auch nur Bash-Builtins verwendet werden

$ 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

Ich sehe keine Vorteile im Vergleich zur Top-Antwort, nur um einen etwas anderen Weg zu zeigen, der für manche Zwecke besser geeignet sein könnte.

mpy
quelle
echoist auch eine in Bash eingebaute Shell (Test type echo).
Daniel Andersson
@DanielAndersson: Klar, deshalb habe ich " auch nur mit bash builtins" geschrieben, wobei ich Ihre nette (bereits +1 gegebene) Antwort im Hinterkopf hatte.
mpy
OK, du kannst meinen Kommentar als Sprachverwirrung einstufen :-).
Daniel Andersson