Bash Script ทำซ้ำทุกคำในบรรทัดหรือไม่


13

ฉันมีสตริงเช่น: dog cat bird whale

และฉันต้องการที่จะได้รับ dog dog cat cat bird bird whale whale

คำทั้งหมดอยู่ในบรรทัดเดียวกัน ความคิดใด ๆ

คำตอบ:


28

การเพิ่มไปยังตระกูลของโซลูชัน :-)

duplicator.sh:

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

ทำให้ปฏิบัติการและตอนนี้:

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

อีกทางเลือกหนึ่งคือฟังก์ชั่นเชลล์เช่นสามารถนำมาใช้ซ้ำได้ภายในสคริปต์:

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

ซึ่งสามารถเรียกใช้โดยตรงซึ่งกำหนดเป็น

duplicator dog cat bird whale

4
ฉันชอบความเรียบง่ายของวิธีการของเชลล์
Henk Langeveld

ฉันชอบความเรียบง่ายของโซลูชันนี้จริงๆ
Cristian

ดีกว่าสองวิธีแรกที่ฉันคิด
Joe

21

คุณสามารถใช้sed:

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

หากคุณต้องการบันทึกการเปลี่ยนแปลงในไฟล์ให้พูดว่า:

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

คุณสามารถใช้perl:

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

(เพิ่ม-iตัวเลือกเพื่อบันทึกการเปลี่ยนแปลงในไฟล์แบบแทนที่)

หรือตามคำแนะนำของterdon :

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

ข้อความจากperlvar:

@F

อาร์เรย์@Fมีฟิลด์ของแต่ละบรรทัดที่อ่านเมื่อเปิดโหมด autosplit ดู perlrun สำหรับ-aสวิตช์ strict 'vars'อาร์เรย์นี้เป็นแพคเกจที่เฉพาะเจาะจงและจะต้องประกาศหรือกำหนดชื่อแพคเกจเต็มถ้าไม่ได้อยู่ในแพคเกจหลักเมื่อทำงานภายใต้


4
sed -r 's/\S+/& &/g'สั้น:
cYrus

2
นั่นไม่ใช่สคริปต์ Bash การเรียกคำสั่งบางคำสั่ง (แม้จาก Bash shell) จะไม่รวมสคริปต์ Bash
Peter Mortensen

4
@PeterMortensen คุณถูกต้องในทางเทคนิค (ชนิดที่ดีที่สุดของความถูกต้อง) แต่ในทางปฏิบัติทุกระบบที่ติดตั้ง bash จะมีเครื่องมือ unix มาตรฐานรวมถึง sed และ awk จุดรวมของการเขียนสคริปต์เชลล์ (ส่วนใหญ่ของจุดนั้น) คือการชี้คำสั่งในตำแหน่งที่ถูกต้อง
evilsoup

3
@PeterMortensen สิ่งนี้ไม่ถูกต้อง สคริปต์ทุบตีสามารถเรียกคำสั่งภายนอก สคริปต์ทุบตีควรเริ่มต้นด้วยบรรทัด shebang แต่ไม่จำเป็นอย่างยิ่ง คำถามไม่ได้ระบุว่าสคริปต์ทุบตีไม่ควรเรียกคำสั่งภายนอก (มักเรียกว่า "สคริปต์ทุบตีบริสุทธิ์")
Gilles 'หยุดความชั่วร้าย'

2
เคล็ดลับ Perl ที่ดี คุณสามารถทำให้มันสั้นลงด้วย-a:perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;'
terdon

4

สิ่งนี้จะไม่มีawk/gawkคำตอบ:

$ 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 

หากการขึ้นบรรทัดใหม่มีความสำคัญ:

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

โหวตครั้งแรกของฉัน แค่อยากรู้ทำไม สคริปต์มีความผิดปกติหรือไม่? หรือรุ่นที่สั้นกว่านี้?
user19087

ไม่ใช่ downvote ของฉัน แต่for(i=1;i<=NF;++i) printf "%s %s ",$i,$i;ทั้งสั้นและอ่านง่ายกว่า IMHO
rici

ยากที่จะโต้แย้งกับสิ่งนั้นดังนั้นฉันจึงทำให้คำตอบของฉันง่ายขึ้นและสั้นลง (ตอนนี้ใช้ประโยชน์จากดัชนีที่ถูกปัดเศษเป็น ints) โดยไม่ต้องเปลี่ยนวิธีการ หวังว่าตอนนี้จะชัดเจนขึ้น
user19087

1
นั่นไม่ใช่สคริปต์ Bash การเรียกคำสั่งบางคำสั่ง (แม้จาก Bash shell) จะไม่รวมสคริปต์ Bash
Peter Mortensen

แม้ว่าการใส่สิ่งนี้ไว้ในสคริปต์นั้นเป็นเรื่องเล็กน้อย
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
ฉันคิดเกี่ยวกับการเขียนsed -n 'p;p'- ฉันคิดว่ามันโปร่งใสมากขึ้นเกี่ยวกับสิ่งที่ทำ
เกล็นแจ็คแมน

1
คุณควรเพิ่มเข้าไปในคำตอบ!
terdon

1

หากคุณมีสตริงอยู่ในตัวแปรfoo="dog cat bird whale"คุณสามารถทำได้ดังนี้

  • ทุบตีบริสุทธิ์:

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

    คำอธิบาย:วงเล็บมีความจำเป็นเพื่อให้readและechoเกิดขึ้นใน subshell เดียวกันและดังนั้นจึงสามารถแบ่งปันตัวแปร หากไม่มีพวกเขาechoก็จะพิมพ์บรรทัดว่างเปล่า

  • 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

    คำอธิบาย:-oธงjoinช่วยให้คุณสามารถตั้งค่ารูปแบบการแสดงผล ที่นี่ฉันจะบอกให้พิมพ์ฟิลด์ที่ 1 ของไฟล์ที่ 1 ( 1.1) ตามด้วยฟิลด์ที่ 2 ของไฟล์ที่ 1 ( 1.2) เป็นต้นวิธีนั้นแต่ละฟิลด์ของไฟล์ที่ 1 จะถูกพิมพ์สองครั้ง อย่างไรก็ตามjoinได้รับการออกแบบให้เข้าร่วมสองบรรทัดอินพุตในฟิลด์ทั่วไป ดังนั้นฉันจึงผ่านมันเป็นบรรทัดว่าง ( <(echo)) แล้วก็เพิกเฉย การ-jตั้งค่าเขตข้อมูลการรวมตั้งค่าเป็นฟิลด์ที่ไม่มีอยู่ (อันดับที่ 5) ทำให้เกิดjoinการพิมพ์ทั้งบรรทัด

    หากคุณไม่สนใจช่องว่างหรือคำสั่งซื้อคุณสามารถทำได้

    $ 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

    คำอธิบาย:

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

    สคริปต์ข้างต้นจะช่วยให้แต่ละเขต (จาก@F) สองครั้งในอาร์เรย์แล้วพิมพ์@k @kหากคุณไม่ต้องการขึ้นบรรทัดใหม่คุณสามารถทำให้ง่ายขึ้น

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

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

    คำอธิบาย:-0ตัวเลือกชุดคั่นบันทึกการป้อนข้อมูล (เป็นเลขฐานสิบหกหรือตัวเลขฐานแปดดูที่นี่สำหรับการแปลง) ที่นี่ฉันจะตั้งค่าให้เป็นฐานแปด040ซึ่งเป็นช่องว่าง -pทำให้perlพิมพ์ป้อนข้อมูล "สาย" แต่ละคนและตั้งแต่ที่เราได้ตั้งค่าการบันทึกแยกไปยังพื้นที่เส้นที่กำหนดไว้ในขณะนี้โดยช่องว่างเพื่อให้ทุกสาขาจะมีการพิมพ์เป็นครั้งที่สอง

  • awk:

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

    คำอธิบาย: NFคือจำนวนฟิลด์ดังนั้นสคริปต์ด้านบนจะผ่านแต่ละฟิลด์และผนวกเข้ากับตัวเอง เมื่อเสร็จแล้วเราจะพิมพ์บรรทัด ( 1;เป็นเพียงการย่อเพื่อพิมพ์)


0

ตอนนี้สำหรับpythonคำตอบ:

จากบรรทัดคำสั่ง:

$ 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

จาก stdin:

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

ผลลัพธ์ในทั้งสองกรณี:

dog dog cat cat bird bird whale whale

0

over-the-top เล็กน้อย แต่haskellคำตอบ:

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

0

อีกวิธีหนึ่งโดยใช้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

ฉันไม่เห็นประโยชน์ใด ๆ เมื่อเปรียบเทียบกับคำตอบยอดนิยมเพียงเพื่อแสดงวิธีที่แตกต่างออกไปเล็กน้อยซึ่งอาจเหมาะกว่าสำหรับวัตถุประสงค์บางอย่าง


echoยังเป็นตัวเชลล์ใน Bash (ทดสอบtype echo)
Daniel Andersson

@DanielAndersson: แน่นอนว่านั่นคือเหตุผลที่ผมเขียนว่า " ยังใช้เพียง builtins ทุบตี" มีความสุขของคุณ (แล้ว +1) คำตอบในใจ
mpy

ตกลงคุณสามารถจำแนกความคิดเห็นของฉันเป็นภาษาที่สับสน :-)
Daniel Andersson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.