จะใช้คำสั่ง shell เพื่อแสดงเฉพาะคอลัมน์แรกและคอลัมน์สุดท้ายในไฟล์ข้อความได้อย่างไร


30

ฉันต้องการความช่วยเหลือในการหาวิธีใช้คำสั่ง sed เพื่อแสดงเฉพาะคอลัมน์แรกและคอลัมน์สุดท้ายในไฟล์ข้อความ นี่คือสิ่งที่ฉันมีจนถึงคอลัมน์ 1:

cat logfile | sed 's/\|/ /'|awk '{print $1}'

ความพยายามที่อ่อนแอของฉันในการทำให้คอลัมน์สุดท้ายแสดงเช่นกันคือ:

cat logfile | sed 's/\|/ /'|awk '{print $1}{print $8}'

อย่างไรก็ตามการดำเนินการนี้จะใช้คอลัมน์แรกและคอลัมน์สุดท้ายแล้วรวมเข้าด้วยกันในรายการเดียว มีวิธีพิมพ์คอลัมน์แรกและคอลัมน์สุดท้ายอย่างชัดเจนด้วยคำสั่ง sed และ awk หรือไม่?

ตัวอย่างอินพุต:

foo|dog|cat|mouse|lion|ox|tiger|bar

5
โปรดป้อนตัวอย่างบางส่วน
jasonwryan

คำตอบ:


51

เกือบจะมี. เพียงแค่ใส่การอ้างอิงคอลัมน์ทั้งสองอยู่ติดกัน

cat logfile | sed 's/|/ /' | awk '{print $1, $8}'

โปรดทราบว่าคุณไม่ต้องการcatที่นี่

sed 's/|/ /' logfile | awk '{print $1, $8}'

นอกจากนี้ยังทราบคุณสามารถบอกได้awkว่าตัวคั่นคอลัมน์|แทนช่องว่างเพื่อให้คุณไม่จำเป็นต้องมีsedอย่างใดอย่างหนึ่ง

awk -F '|' '{print $1, $8}' logfile

ตามข้อเสนอแนะโดยCalebหากคุณต้องการโซลูชันที่ยังคงแสดงผลฟิลด์สุดท้ายแม้ว่าจะมีไม่ถึงแปดก็ตามคุณก็สามารถใช้งาน$NFได้

awk -F '|' '{print $1, $NF}' logfile

นอกจากนี้หากคุณต้องการให้เอาต์พุตเก็บรักษา|ตัวคั่นแทนที่จะใช้ช่องว่างคุณสามารถระบุตัวคั่นฟิลด์เอาต์พุต น่าเสียดายที่มันค่อนข้างงุ่มง่ามกว่าการใช้-Fธง แต่นี่คือสามวิธี

  • คุณสามารถกำหนดตัวคั่นฟิลด์อินพุตและเอาต์พุตawkในบล็อก BEGIN

    awk 'BEGIN {FS = OFS = "|"} {print $1, $8}' logfile
  • คุณสามารถกำหนดตัวแปรเหล่านี้เมื่อโทรawkจากบรรทัดคำสั่งผ่าน-vแฟล็ก

    awk -v 'FS=|' -v 'OFS=|' '{print $1, $8}' logfile
  • หรือเพียงแค่:

    awk -F '|' '{print $1 "|" $8}' logfile

4
ทำได้ดีมากสรุปได้ว่าปัญหานี้สามารถลดความซับซ้อนได้อย่างไร คุณอาจเพิ่มหมายเหตุเกี่ยวกับวิธีใช้|เป็นตัวคั่นเอาต์พุตแทนการเว้นวรรคดีฟอลต์สำหรับการต่อข้อมูลสตริง นอกจากนี้คุณสามารถอธิบายการใช้$NFแทนการเข้ารหัสอย่างหนัก$8เพื่อรับคอลัมน์สุดท้าย
คาเลบ

12

เพียงแทนที่จากคนแรกไปเป็นครั้งสุดท้าย|ด้วย|(หรือเว้นวรรคหากคุณต้องการ):

sed 's/|.*|/|/'

หมายเหตุว่าแม้ไม่มีsedการดำเนินการที่|เป็นพิเศษ (ตราบเท่าที่การขยายการแสดงออกปกติไม่ได้เปิดใช้งานผ่าน-Eหรือ-rในการใช้งานบางส่วน) \|ตัวเองเป็นพิเศษในบางอย่างเช่น sedGNU ดังนั้นคุณไม่ควรหลบหนี|ถ้าคุณตั้งใจจะจับคู่|ตัวละคร

หากแทนที่ด้วยช่องว่างและหากการป้อนข้อมูลอาจมีเส้นที่มีเพียงบรรทัดเดียวอยู่|แล้วคุณจะต้องปฏิบัติต่อสิ่งนั้นเป็นพิเศษเพราะ|.*|จะไม่ตรงกันกับสิ่งเหล่านั้น นั่นอาจเป็น:

sed 's/|\(.*|\)\{0,1\}/ /'

(นั่นเป็น.*|ส่วนเสริม) หรือ:

sed 's/|.*|/ /;s/|/ /'

หรือ:

sed 's/\([^|]*\).*|/\1 /'

หากคุณต้องการฟิลด์แรกและฟิลด์ที่แปดโดยไม่คำนึงถึงจำนวนฟิลด์ในอินพุตนั่นเป็นเพียง:

cut -d'|' -f1,8


(ทุกคนที่จะทำงานกับยูทิลิตี้ตามมาตรฐาน POSIX สมมติว่ารูปแบบการป้อนข้อความที่ถูกต้อง (โดยเฉพาะอย่างยิ่งsedคนที่โดยทั่วไปจะไม่ทำงานถ้าใส่มีไบต์หรือลำดับของไบต์ที่ไม่ได้แบบตัวอักษรที่ถูกต้องในสถานที่ปัจจุบันเหมือนเช่นprintf 'unix|St\351phane|Chazelas\n' | sed 's/|.*|/|/'ใน โลแคล UTF-8))


11

คุณกำลังใช้งานawkอยู่:

awk '{ print $1, $NF }' file

2
คุณไม่จำเป็นต้องระบุตัวคั่นฟิลด์อินพุต (เนื่องจากในกรณีนี้ดูเหมือนว่าเป็น|พื้นที่ว่างนั้น) ด้วย-F\|หรือคล้ายกัน นอกจากนี้ถ้าเขาต้องการใช้ตัวคั่นเดียวกันสำหรับเอาต์พุต
Caleb

@Caleb อาจ: ผมกำลังรอ OP เพื่อยืนยันสิ่งที่ตรงกับการป้อนข้อมูลที่ดูเหมือนจะมากกว่าการพยายามที่จะคาดเดาอยู่บนพื้นฐานของตัวอย่างที่ไม่ทำงาน ...
jasonwryan

1
โปรดทราบว่าสมมติว่าอินพุตมีอย่างน้อย 2 ฟิลด์
Stéphane Chazelas

@ StéphaneChazelas OP ระบุไว้อย่างชัดเจนในโค้ดว่ามีแปดฟิลด์เสมอ
michaelb958 - Reinstate Monica

3
@ michaelb958 ผมคิดว่า "อย่างชัดเจน" จะเกินกรณีที่เพียงเล็ก ๆ น้อย ๆ :)
jasonwryan

4

หากคุณพบว่าตัวเองตกใจและเฉื่อยน้อยคุณสามารถทำสิ่งเดียวกันได้ด้วยคอร์คูล:

paste <(           cut -d'|' -f1  file) \ 
      <(rev file | cut -d'|' -f1 | rev)

cutสะอาดและกะทัดรัดกว่า awk / sed เมื่อคุณสนใจในคอลัมน์แรกหรือถ้า delimeter ได้รับการแก้ไข (เช่นไม่ใช่จำนวนตัวแปรของช่องว่าง)
Sridhar Sarnobat

2

|ดูเหมือนว่าคุณกำลังพยายามที่จะได้รับสาขาแรกและครั้งสุดท้ายของข้อความซึ่งจะถูกคั่นด้วย

ฉันถือว่าไฟล์บันทึกของคุณมีข้อความเหมือนด้านล่าง

foo|dog|cat|mouse|lion|ox|tiger|bar
bar|dog|cat|mouse|lion|ox|tiger|foo

และคุณต้องการผลลัพธ์เช่น

foo bar
bar foo

ถ้าใช่แล้วคำสั่งของคุณก็มาที่นี่

ผ่าน GNU sed

sed -r 's~^([^|]*).*\|(.*)$~\1 \2~' file

ตัวอย่าง:

$ echo 'foo|dog|cat|mouse|lion|ox|tiger|bar' | sed -r 's~^([^|]*).*\|(.*)$~\1 \2~'
foo bar

คอลัมน์ไม่ได้ถูกคั่นด้วยไพพ์ | แต่พวกเขาอยู่ในคอลัมน์ฉันสนใจที่จะใช้ sed แต่ไม่ได้ใช้คำสั่ง awk เหมือนที่คุณทำในคำสั่งของคุณ: sed -r 's ~ ^ ([^ |] *). * \ | (. *) $ ~ \ ไฟล์ 1 \ 2 ~ '
70573

"คอลัมน์ไม่ได้ถูกคั่นด้วยไพพ์ | แต่อยู่ในคอลัมน์" คุณหมายถึงคอลัมน์ที่คั่นด้วยช่องว่างหรือไม่
Avinash Raj

ตัวอย่างอินพุตและเอาต์พุตจะดีกว่า
Avinash Raj

1

คุณอาจจะทำกับsed- ฉันจะ - แต่เพียงทำให้ไม่มีใครเขียนอันนี้เลย:

while IFS=\| read col1 cols
do  printf %10s%-s\\n "$col1 |" " ${cols##*|}"
done <<\INPUT
foo|dog|cat|mouse|lion|ox|tiger|bar
INPUT

เอาท์พุท

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