Bash Script để lặp lại từng từ trong một dòng?


13

Tôi có một chuỗi như: dog cat bird whale

Và tôi muốn có được dog dog cat cat bird bird whale whale

Tất cả các từ trong cùng một dòng. Bất kỳ ý tưởng?

Câu trả lời:


28

Thêm vào gia đình của các giải pháp :-).

duplicator.sh:

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

Thực hiện và bây giờ:

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

Ngoài ra, dưới dạng hàm shell, ví dụ: có thể sử dụng lại trong tập lệnh:

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

mà sau đó có thể được chạy trực tiếp ở nơi được định nghĩa là

duplicator dog cat bird whale

4
Tôi thích sự đơn giản của phương pháp vỏ.
Henk Langeveld

Chà, tôi thực sự yêu thích sự đơn giản của giải pháp này
Cristian

Cách tốt hơn so với vài cách đầu tiên tôi nghĩ đến.
Joe

21

Bạn có thể sử dụng sed:

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

Nếu bạn muốn lưu các thay đổi vào tệp tại chỗ, hãy nói:

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

Bạn cũng có thể sử dụng perl:

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

(Thêm -itùy chọn để lưu các thay đổi vào tệp tại chỗ.)

Hoặc, theo đề xuất của terdon :

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

Trích dẫn từ perlvar:

@F

Mảng @Fchứa các trường của mỗi dòng được đọc khi chế độ tự động bật được bật. Xem perlrun cho -achuyển đổi. Mảng này là đặc thù của gói và phải được khai báo hoặc đặt tên gói đầy đủ nếu không có trong gói chính khi chạy bên dưới strict 'vars'.


4
Ngắn hơn : sed -r 's/\S+/& &/g'.
cYrus

2
Đó không phải là một kịch bản Bash. Gọi một số lệnh (thậm chí từ shell Bash) không bao gồm tập lệnh Bash.
Peter Mortensen

4
@PeterMortensen Bạn đúng về mặt kỹ thuật (loại chính xác nhất), nhưng thực tế mọi hệ thống đã cài đặt bash cũng sẽ có các công cụ unix tiêu chuẩn, bao gồm cả sed và awk. Toàn bộ điểm của kịch bản shell (tốt, một phần lớn của điểm của nó) là trỏ các lệnh vào đúng vị trí.
evilsoup

3
@PeterMortensen Điều này không chính xác. Một tập lệnh bash có thể gọi các lệnh bên ngoài. Một tập lệnh bash nên bắt đầu bằng một dòng shebang, nhưng điều này không thực sự cần thiết. Câu hỏi đã không xác định rằng tập lệnh bash không nên gọi các lệnh bên ngoài (thường được gọi là tập lệnh bash thuần túy).
Gilles 'SO- ngừng trở nên xấu xa'

2
Thủ thuật perl đẹp. Bạn có thể làm cho nó ngắn hơn với -a:perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;'
terdon

4

Điều này sẽ là gì nếu không có awk/gawkcâu trả lời:

$ 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 

Nếu một dòng mới chấm dứt là quan trọng:

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

Downvote đầu tiên của tôi. Chỉ tò mò, tại sao? Có điều gì sai với kịch bản? Hoặc một phiên bản rút gọn hơn?
dùng19087

Không phải downvote của tôi, nhưng for(i=1;i<=NF;++i) printf "%s %s ",$i,$i;cả ngắn hơn và dễ đọc hơn, IMHO.
rici

Khó có thể tranh luận với điều đó, vì vậy tôi đã đơn giản hóa và rút ngắn câu trả lời của mình (bây giờ tận dụng các chỉ số được làm tròn xuống ints) mà không thay đổi cách tiếp cận. Hy vọng nó bây giờ rõ ràng hơn.
user19087

1
Đó không phải là một kịch bản Bash. Gọi một số lệnh (thậm chí từ shell Bash) không bao gồm tập lệnh Bash.
Peter Mortensen

Đặt điều này trong một kịch bản là tầm thường, mặc dù.
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
Tôi nghĩ về việc viết lách sed -n 'p;p'- tôi nghĩ điều đó minh bạch hơn về những gì nó đang làm.
glenn jackman

1
Bạn nên thêm nó vào câu trả lời!
terdon

1

Nếu bạn có chuỗi của bạn trong một biến, giả sử foo="dog cat bird whale", bạn có thể làm:

  • Bash tinh khiết:

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

    Giải thích: Các dấu ngoặc đơn là cần thiết để readechoxảy ra trong cùng một mạng con và do đó có thể chia sẻ các biến. Nếu không có họ, họ echosẽ chỉ in một dòng trống.

  • lõi:

    $ 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

    Giải thích: Các -olá cờ của joincho phép bạn thiết lập các định dạng đầu ra. Ở đây, tôi đang bảo nó in trường thứ nhất của tệp 1 ( 1.1), tiếp theo là trường thứ 2 của tệp thứ 1 ( 1.2), v.v ... Bằng cách đó, mỗi trường của tệp thứ 1 được in hai lần. Tuy nhiên, joinđược thiết kế để, tốt, tham gia hai dòng đầu vào trên một trường chung. Vì vậy, tôi cũng chuyển cho nó một dòng trống ( <(echo)) và sau đó bỏ qua nó. Trường -jđặt trường tham gia, đặt trường này thành trường không tồn tại (lần thứ 5) joinđể in toàn bộ dòng.

    Nếu bạn không quan tâm đến khoảng trắng hoặc thứ tự đầu vào, bạn có thể làm

    $ 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

    Giải trình:

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

    Kịch bản trên sẽ lưu mỗi trường (từ @F) hai lần trong mảng @kvà sau đó in @k. Nếu bạn không cần dòng mới, bạn có thể đơn giản hóa

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

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

    Giải thích: Các -0tùy chọn thiết lập tách hồ sơ đầu vào (như là một hệ thập lục phân hoặc số bát phân, xem ở đây cho các chuyển đổi). Ở đây, tôi đang đặt nó thành bát phân 040là một không gian. Việc in -pgiúp perlmỗi "dòng" đầu vào và vì chúng ta đã đặt dấu tách bản ghi thành khoảng trắng, các dòng hiện được xác định bởi khoảng trắng, vì vậy mỗi trường được in hai lần.

  • awk:

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

    Giải thích: NF là số lượng các trường, do đó, tập lệnh ở trên đi qua từng trường và nối nó với chính nó. Khi đã xong, chúng tôi in dòng ( 1;chỉ là tốc ký để in).


0

Bây giờ cho một pythoncâu trả lời:

Từ dòng lệnh:

$ 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

Từ stdin:

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

Kết quả trong cả hai trường hợp:

dog dog cat cat bird bird whale whale

0

Hơi quá đầu, nhưng một haskellcâu trả lời:

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

0

Một cách tiếp cận khác, cũng chỉ sử dụng các nội dung 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

Tôi không thấy bất kỳ lợi ích nào so với câu trả lời hàng đầu, chỉ để thể hiện một cách hơi khác, có thể phù hợp hơn cho một số mục đích.


echocũng là một shell dựng sẵn trong Bash (thử nghiệm type echo).
Daniel Andersson

@DanielAndersson: Chắc chắn, đó là lý do tại sao tôi đã viết " cũng chỉ sử dụng các nội dung bash", có sẵn câu trả lời (đã + 1'd) tốt đẹp của bạn.
mpy

OK, bạn có thể phân loại nhận xét của tôi là nhầm lẫn ngôn ngữ :-).
Daniel Andersson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.