Bash Script per ripetere ogni parola in una riga?


13

Ho una stringa come: dog cat bird whale

E voglio ottenere dog dog cat cat bird bird whale whale

Tutte le parole sono nella stessa riga. Qualche idea?

Risposte:


28

Aggiungendo alla famiglia di soluzioni :-).

duplicator.sh:

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

Rendi eseguibile e ora:

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

In alternativa come funzione shell, ad esempio per essere riutilizzabile all'interno di uno script:

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

che può quindi essere eseguito direttamente dove definito come

duplicator dog cat bird whale

4
Mi piace la semplicità dell'approccio shell.
Henk Langeveld

Bene, adoro la semplicità di questa soluzione
Cristian

Molto meglio dei primi due modi in cui ho pensato.
Joe

21

Puoi usare sed:

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

Se si desidera salvare le modifiche sul file sul posto, dire:

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

Puoi anche usare perl:

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

(Aggiungi l' -iopzione per salvare le modifiche al file sul posto.)

Oppure, come suggerito da Terdon :

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

Citando da perlvar:

@F

L'array @Fcontiene i campi di ciascuna riga letta quando è attivata la modalità autosplit. Vedi perlrun per l' -ainterruttore. Questo array è specifico del pacchetto e deve essere dichiarato o assegnato un nome pacchetto completo se non in pacchetto principale quando è in esecuzione sotto strict 'vars'.


4
Shorter: sed -r 's/\S+/& &/g'.
cYrus

2
Questo non è uno script Bash. Invocare alcuni comandi (anche da una shell Bash) non consiste in uno script Bash.
Peter Mortensen,

4
@PeterMortensen Sei tecnicamente corretto (il miglior tipo di correzione), ma praticamente ogni sistema che ha installato bash avrà anche gli strumenti unix standard, inclusi sed e awk. L'intero punto dello scripting della shell (beh, gran parte di esso) è puntare i comandi nel posto giusto.
evilsoup,

3
@PeterMortensen Questo non è corretto. Uno script bash può chiamare comandi esterni. Uno script bash dovrebbe iniziare con una riga shebang, ma ciò non è strettamente necessario. La domanda non specificava che lo script bash non dovesse chiamare comandi esterni (spesso chiamato "script bash puro").
Gilles 'SO-smetti di essere malvagio'

2
Bel trucco perl. Puoi accorciarlo con -a:perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;'
terdon

4

Cosa sarebbe questo senza una awk/gawkrisposta:

$ 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 una riga di chiusura è importante:

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

I miei primi voti negativi. Solo curioso, perché? C'è qualcosa che non va nella sceneggiatura? O una versione più abbreviata?
user19087

Non il mio downvote, ma for(i=1;i<=NF;++i) printf "%s %s ",$i,$i;è sia più breve che più leggibile, IMHO.
rici,

Difficile discuterne, quindi ho semplificato e abbreviato la mia risposta (ora sfrutta gli indici arrotondati per difetto) senza cambiare l'approccio. Spero che ora sia più chiaro.
user19087

1
Questo non è uno script Bash. Invocare alcuni comandi (anche da una shell Bash) non consiste in uno script Bash.
Peter Mortensen,

Metterlo in una sceneggiatura è però banale.
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
Ho pensato di scrivere sed -n 'p;p'- ho pensato che fosse più trasparente su ciò che sta facendo.
Glenn Jackman,

1
Dovresti aggiungerlo alla risposta!
Terdon,

1

Se hai la tua stringa in una variabile, ad esempio foo="dog cat bird whale", potresti fare:

  • Bash 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

    Spiegazione: Le parentesi sono necessarie affinché la reade echoavvengano nella stessa sottostruttura e possano quindi condividere le variabili. Senza di essi, echosi stamperebbe solo una riga vuota.

  • 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

    Spiegazione: Il -oflag di joinconsente di impostare il formato di output. Qui, gli sto dicendo di stampare il 1 ° campo del 1o file ( 1.1), seguito dal 2o campo del 1o file ( 1.2) ecc. In questo modo ogni campo del 1o file viene stampato due volte. Tuttavia, joinè progettato per unire due linee di input su un campo comune. Quindi, gli passo anche una riga vuota ( <(echo)) e quindi la ignoro. I -jset le uniscono campo, impostandolo a uno che non esiste (il 5 °) causa joinper stampare l'intera linea.

    Se non ti interessa lo spazio bianco o l'ordine di input, puoi farlo

    $ 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

    Spiegazione:

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

    Lo script sopra salverà ogni campo (da @F) due volte nell'array @ke quindi stamperà @k. Se non hai bisogno della nuova riga finale, puoi semplificare

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

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

    Spiegazione: L' -0opzione imposta il separatore del record di input (come numero esadecimale o ottale, vedere qui per le conversioni). Qui, lo sto impostando su ottale 040che è uno spazio. I -pmarchi perlstampano ogni ingresso "line" e poiché abbiamo impostato il separatore di record di spazio, le linee sono ora definiti da spazi, così ogni campo viene stampata due volte.

  • awk:

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

    Spiegazione: NF è il numero di campi, quindi lo script sopra passa attraverso ogni campo e lo aggiunge a se stesso. Una volta fatto, stampiamo la linea ( 1;è solo una scorciatoia per la stampa).


0

Ora per una pythonrisposta:

Dalla riga di 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

Dallo stdin:

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

Il risultato in entrambi i casi:

dog dog cat cat bird bird whale whale

0

Leggermente esagerato, ma una haskellrisposta:

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

0

Un altro approccio, usando anche solo builtin 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

Non vedo alcun vantaggio rispetto alla risposta principale, solo per mostrare un modo leggermente diverso, che potrebbe essere più adatto per alcuni scopi.


echoè anche una shell integrata in Bash (test type echo).
Daniel Andersson,

@DanielAndersson: Certo, è per questo che ho scritto " anche usando solo i builtin bash", avendo in mente la tua bella risposta (già +1).
MP

OK, puoi classificare il mio commento come confusione linguistica :-).
Daniel Andersson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.