Bash Script untuk mengulang setiap kata dalam satu baris?


13

Saya memiliki string seperti: dog cat bird whale

Dan saya ingin mendapatkannya dog dog cat cat bird bird whale whale

Semua kata berada di baris yang sama. Ada ide?

Jawaban:


28

Menambah keluarga solusi :-).

duplicator.sh:

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

Jadikan dapat dieksekusi, dan sekarang:

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

Atau sebagai fungsi shell, mis. Agar dapat digunakan kembali dalam skrip:

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

yang kemudian dapat dijalankan langsung di mana didefinisikan sebagai

duplicator dog cat bird whale

4
Saya suka kesederhanaan pendekatan shell.
Henk Langeveld

Ya saya sangat menyukai kesederhanaan dari solusi ini
Cristian

Jauh lebih baik daripada beberapa cara pertama yang saya pikirkan.
Joe

21

Anda bisa menggunakan sed:

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

Jika Anda ingin menyimpan perubahan ke file di tempat, katakan:

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

Anda juga bisa menggunakan perl:

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

(Tambahkan -iopsi untuk menyimpan perubahan ke file di tempat.)

Atau, seperti yang disarankan oleh terdon :

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

Mengutip dari perlvar:

@F

Array @Fberisi bidang setiap baris yang dibaca ketika mode autosplit dihidupkan. Lihat perlrun untuk -asakelar. Array ini khusus untuk paket, dan harus dideklarasikan atau diberi nama paket lengkap jika tidak dalam paket utama saat dijalankan strict 'vars'.


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

2
Itu bukan skrip Bash. Menjalankan beberapa perintah (bahkan dari shell Bash) tidak termasuk skrip Bash.
Peter Mortensen

4
@PeterMortensen Anda secara teknis benar (jenis terbaik yang benar), tetapi praktis setiap sistem yang telah menginstal bash juga akan memiliki alat unix standar, termasuk sed dan awk. Inti dari skrip shell (well, sebagian besar pointnya) adalah mengarahkan perintah di tempat yang tepat.
evilsoup

3
@PeterMortensen Ini tidak benar. Skrip bash dapat memanggil perintah eksternal. Skrip bash harus dimulai dengan baris shebang, tetapi ini tidak sepenuhnya diperlukan. Pertanyaannya tidak menentukan bahwa skrip bash tidak boleh memanggil perintah eksternal (sering disebut "skrip bash murni").
Gilles 'SO- stop being evil'

2
Trik perl yang bagus. Anda dapat membuatnya lebih pendek dengan -a:perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;'
terdon

4

Apa ini tanpa awk/gawkjawaban:

$ 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 

Jika penghentian baris baru penting:

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

Suara saya yang pertama downvote. Penasaran saja, mengapa? Apakah ada yang salah dengan skrip? Atau versi yang lebih singkat?
user19087

Bukan downvote saya, tetapi for(i=1;i<=NF;++i) printf "%s %s ",$i,$i;keduanya lebih pendek dan lebih mudah dibaca, IMHO.
rici

Sulit untuk berdebat dengan itu, jadi saya telah menyederhanakan dan mempersingkat jawaban saya (sekarang mengambil keuntungan dari indeks dibulatkan ke int) tanpa mengubah pendekatan. Semoga sekarang lebih jelas.
user19087

1
Itu bukan skrip Bash. Menjalankan beberapa perintah (bahkan dari shell Bash) tidak terdiri dari skrip Bash.
Peter Mortensen

Menempatkan ini dalam sebuah skrip itu sepele.
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
Saya berpikir tentang menulis sed -n 'p;p'- saya pikir itu lebih transparan tentang apa yang dilakukannya.
glenn jackman

1
Anda harus menambahkan itu ke jawabannya!
terdon

1

Jika Anda memiliki string dalam suatu variabel, katakanlah foo="dog cat bird whale", Anda bisa melakukan:

  • Pesta murni:

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

    Penjelasan: Tanda kurung diperlukan agar readdan echoterjadi dalam subkulit yang sama dan karenanya dapat berbagi variabel. Tanpa mereka, echohanya akan mencetak garis kosong.

  • 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

    Penjelasan: The -oBendera joinmemungkinkan Anda untuk mengatur format output. Di sini, saya mengatakannya untuk mencetak bidang 1 file 1 ( 1.1), diikuti oleh bidang 2 file 1 ( 1.2) dll. Dengan begitu setiap bidang file 1 dicetak dua kali. Namun, joindirancang untuk, well, bergabung dengan dua jalur input pada bidang yang sama. Jadi, saya juga memberikannya baris kosong ( <(echo)) dan kemudian mengabaikannya. The -jset bergabung lapangan, pengaturan untuk salah satu yang tidak ada (ke-5) penyebabjoin untuk mencetak seluruh baris.

    Jika Anda tidak peduli dengan spasi putih atau urutan input, Anda bisa melakukannya

    $ 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

    Penjelasan:

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

    Script di atas akan menyimpan setiap bidang (dari @F) dua kali dalam array @kdan kemudian mencetak @k. Jika Anda tidak memerlukan trailing newline, Anda dapat menyederhanakannya

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

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

    Penjelasan: The -0option menetapkan pemisah record input (sebagai heksadesimal atau nomor oktal, lihat di sini untuk konversi). Di sini, saya mengaturnya ke oktal 040yang merupakan ruang. The -pmerek perlmencetak setiap masukan "line" dan karena kami telah menetapkan rekor pemisah ruang, garis sekarang didefinisikan oleh spasi, sehingga setiap bidang dicetak dua kali.

  • awk:

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

    Penjelasan: NF adalah jumlah bidang, jadi skrip di atas melewati setiap bidang dan menambahkannya sendiri. Setelah selesai, kami mencetak baris ( 1;hanya singkatan untuk cetak).


0

Sekarang untuk pythonjawabannya:

Dari baris perintah:

$ 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

Dari stdin:

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

Hasilnya dalam kedua kasus:

dog dog cat cat bird bird whale whale

0

Sedikit berlebihan, tetapi sebuah haskelljawaban:

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

0

Pendekatan lain, juga hanya menggunakan 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

Saya tidak melihat manfaat apa pun dibandingkan dengan jawaban teratas, hanya untuk menunjukkan cara yang sedikit berbeda, yang mungkin lebih cocok untuk beberapa tujuan.


echojuga merupakan builtin shell di Bash (test type echo).
Daniel Andersson

@DanielAndersson: Tentu, itu sebabnya saya menulis " juga hanya menggunakan bash builtins", mengingat jawaban Anda yang bagus (sudah diberi +1).
mpy

OK, Anda dapat mengklasifikasikan komentar saya sebagai kebingungan bahasa :-).
Daniel Andersson
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.