แทนที่อักขระยกเว้นการเกิด x ครั้งล่าสุด


9

ฉันมีไฟล์ที่มีชื่อโฮสต์มากมายสัมพันธ์กับ IP ที่มีลักษณะดังนี้:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int.test.example.com 59.2.86.3
super.awesome.machine 123.234.15.6

ฉันอยากให้มันเป็นแบบนี้:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int-test-example-com 59.2.86.3
super-awesome-machine 123.234.15.6

ฉันจะแทนที่ (จุด) จากคอลัมน์แรกด้วย - (เครื่องหมายขีดกลาง) เพื่ออำนวยความสะดวกในการจัดเรียงตามคอลัมน์ที่สอง? ฉันกำลังคิดที่จะใช้ sed เพื่อแทนที่จุดจนถึงช่องว่างแรกหรือแทนที่จุดทุกจุดยกเว้นสามจุดสุดท้าย แต่ฉันมีปัญหาในการเข้าใจ regex และ sed ฉันสามารถทำการทดแทนแบบง่าย ๆ แต่นี่เป็นวิธีเหนือหัวของฉัน!

นี่เป็นส่วนหนึ่งของสคริปต์ที่ใหญ่กว่าที่ฉันเขียนด้วยการทุบตี ฉันติดอยู่ที่ส่วนนี้

คำตอบ:


7

คุณสามารถใช้ AWK

awk '{gsub(/-/,".",$1);print}' infile

คำอธิบาย

awkแยกบรรทัดบนช่องว่างตามค่าเริ่มต้น ดังนั้นคอลัมน์แรกของบรรทัด ( $1in awk-ese) จะเป็นคอลัมน์ที่คุณต้องการทำการแทนที่ เพื่อจุดประสงค์นี้คุณสามารถใช้:

 gsub(regex,replacement,string)

เพื่อดำเนินการทดแทนที่จำเป็น

ทราบว่าgsubได้รับการสนับสนุนเฉพาะgawkและnawkแต่ใน distros หลายสมัยawkเป็น softlink gawkไป


1
+1 เอาชนะฉัน ฉันคิดว่าคำอธิบายจะเป็นประโยชน์ต่อผู้ถามและผู้อ่านในอนาคตเช่นกัน
โจเซฟอาร์

1
@JosephR ขออภัยฉันไม่เก่งในการอธิบาย แต่ฉันได้ลองและอัปเดตแล้ว ..
Rahul Patil

2
ข้อมูลจำเพาะ POSIX สำหรับawkอยู่บนพื้นฐานnawkดังนั้นสิ่งที่ทันสมัยใช้งานควรจะมีawk gsubบน Solaris คุณอาจต้องหรือ/usr/xpg4/bin/awk nawk
Stéphane Chazelas

@RahulPatil ถ้าคุณไม่รังเกียจฉันเพิ่มสองสามบรรทัดที่ฉันคิดว่าจะช่วยเหลือผู้อื่น
โจเซฟอาร์

@JosephR ขอบคุณ .. ดูเหมือนว่าจะสมบูรณ์แบบในตอนนี้ .. :)
Rahul Patil

6

หากคุณจำเป็นต้องทำการแทนที่ในฟิลด์แรกวิธีที่ดีที่สุดคือใช้โซลูชัน awk ของ Rahulแต่ระวังว่ามันอาจส่งผลกระทบต่อการเว้นวรรค

คุณสามารถหลีกเลี่ยงได้โดยเขียนแทน:

perl -pe 's|\S+|$&=~tr/./-/r|e' file

-pธงหมายถึง "อ่านบรรทัดแฟ้มใส่โดยสายและพิมพ์แต่ละบรรทัดหลังจากการใช้สคริปต์ที่กำหนดโดย-e" จากนั้นแทน ( s|pattern|replacement|) ลำดับแรกของอักขระที่ไม่ใช่พื้นที่ ( \S+) ที่มีรูปแบบการจับคู่ ( $&) หลังจากแทนทั้งหมดด้วย. -เคล็ดลับคือการใช้s|||eที่eผู้ประกอบการจะประเมินการแสดงออกแทน ดังนั้นคุณสามารถมีการแทนที่หนึ่งรายการ ( tr/./-/) นำไปใช้กับการจับคู่ ( $&) ของการแทนที่ก่อนหน้า ( s|||e)

หากคุณต้องการทดแทนทุกสิ่ง.ด้วย 3 ข้อ-ยกเว้นล่าสุดด้วย GNU sedและสมมติว่าคุณมีrevคำสั่ง:

rev file | sed 's/\./-/4g' | rev

1
โปรดทราบว่าวิธีการแก้ปัญหา Perl ถือว่ารุ่น 5.14 หรือสูงกว่า (สำหรับการ/rทำงาน)
โจเซฟอาร์

3

Sed ไม่ใช่เครื่องมือที่ง่ายที่สุดสำหรับงาน - ดูคำตอบอื่น ๆ สำหรับเครื่องมือที่ดีกว่า - แต่สามารถทำได้

หากต้องการแทนที่.ด้วย-ช่องว่างแรกเท่านั้นให้ใช้sในการวนซ้ำ

sed -e '
  : a                     # Label "a" for the branching command
  s/^\([^ .]*\)\./\1-/    # If there is a "." before the first space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

(โปรดทราบว่าการปรับใช้บางอย่างไม่สนับสนุนความคิดเห็นในบรรทัดเดียวกัน GNU sed ทำ)

หากต้องการดำเนินการเปลี่ยนจนถึงพื้นที่สุดท้ายแทน:

sed -e '
  : a                     # Label "a" for the branching command
  s/\.\(.* \)/-\1/        # If there is a "." before the last space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

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

sed -e '
  h           # Save the current line to the hold space
  s/.* / /    # Remove everything up to the last space
  x           # Swap the work space with the hold space
  s/[^ ]*$//  # Remove everything after the last space
  y/./-/      # Replace all "." by "-"
  G           # Append the content of the hold to the work space
  s/\n//      # Remove the newline introduced by G
'

2

เนื่องจากราหุลให้คำตอบที่เป็นที่ยอมรับสำหรับกรณีการใช้งานของคุณฉันคิดว่าฉันจะใช้วิธีการตอบคำถามย้ำ: แทนที่ทั้งหมด แต่เกิดขึ้นเมื่อ x ของ regex:

perl -pe '
    $count = tr{.}{.}; # Count '.' on the current line
    $x = 3;
    next LINE if $count <= $x;
    while(s{\.}{-}){   # Substitute one '.' with a '-'
        last if ++$i == $count - $x # Quit the loop before the last x substitutions
    }
$i = 0
' your_file

รหัสข้างต้น (ทดสอบ) ไม่ถือว่าคุณมีเขตข้อมูลที่คั่นด้วยช่องว่าง มันจะแทนที่จุดทั้งหมดในบรรทัดด้วยขีดกลางยกเว้น 3 จุดสุดท้าย แทนที่3ในรหัสตามความชอบของคุณ


2

คุณสามารถใช้เครื่องมือต่าง ๆ สำหรับสิ่งนี้ Rahul Patil มอบgawkหนึ่งให้แก่คุณดังนั้นนี่คือบางส่วน:

  • Perl

    perl -lane  '$F[0]=~s/\./-/g; print "@F"' file
    

    -aสวิตช์สาเหตุ Perl @Fเพื่อเส้นที่นำเข้าแยกโดยอัตโนมัติในช่องว่างและบันทึกฟิลด์ผลในการเข้าแถว ดังนั้นฟิลด์แรกจะเป็น$F[0]ดังนั้นเราจึงแทนที่ ( s///) การเกิดขึ้นทั้งหมดของ.ด้วย-ในฟิลด์แรกแล้วพิมพ์อาร์เรย์ทั้งหมด

  • เปลือก

     while read -r a b; do printf "%s %s\n" "${a//./-}" "$b"; done < file 
    

    นี่ห่วงในขณะที่อ่านแฟ้มโดยอัตโนมัติและแยกบน whitespace.This สร้างสองเขตข้อมูลและ$first $restสร้าง${first//pattern/replacement}แทนที่เกิดขึ้นทั้งหมดด้วยpatternreplacement


+1 ในขณะที่perlrun(1)จะบอกคุณว่า-a"โหมด autosplit" ฉันชอบคิดว่าเป็น " awkโหมด": D
Joseph R.

2

ฉันเชื่อว่านี่อ่านง่ายกว่า regex ที่น่ารังเกียจมาก โดยทั่วไปฉันเพิ่งแยกบรรทัดออกเป็นสองเขตข้อมูลที่ช่องว่างและใช้ sed ในส่วนแรก

while read -r host ip; do
    echo "$(sed 's/\./-/g' <<< "$host") $ip"
done < input_file

คุณสามารถใช้ $ {host //./-} แทนคำสั่ง sed ทั้งนี้ขึ้นอยู่กับเชลล์ของคุณ


0
sed 's/\./-/' <file name>

โดยไม่ต้องใช้gเมื่อสิ้นสุดคำสั่งคุณสามารถทำได้ ... นี่จะเป็นการแทนที่การเกิดครั้งที่ 1 ของรูปแบบ

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