จะพิมพ์เฉพาะคอลัมน์สุดท้ายได้อย่างไร


25
echo -e 'one two three\nfour five six\nseven eight nine'
one two three
four five six
seven eight nine

ฉันจะทำ "MAGIC" บางอย่างเพื่อรับเอาต์พุตนี้ได้อย่างไร:

three
six
nine

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


2
แลนซ์โปรดค้นคว้าคำถามของคุณก่อนถาม การค้นหา google สำหรับหัวเรื่องของโพสต์ของคุณแสดงคำตอบในตัวอย่าง การค้นหา "awk last column" ให้คำตอบที่ยอดเยี่ยมหลายคำเริ่มต้นด้วยผลลัพธ์ 1 นอกจากนี้ไพรเมอร์ awk 5 นาทีนี้มีค่าที่ควรอ่านตลอดทางเพื่อให้คุณรู้ว่าเป็นไปได้ในอนาคต
Caleb

คำตอบ:


6

สามารถทำได้เฉพาะกับ'bash', ไม่มี'sed', 'awk'หรือ'perl':

echo -e 'one two three\nfour five six\nseven eight nine' |
  while IFS=" " read -r -a line; do
    nb=${#line[@]}
    echo ${line[$((nb - 1))]}
  done

อืมหรือสมมุติว่าอินพุตของคุณเป็นช่องว่างจริงๆแล้ว... | while read -r line; do echo ${line##* }; done
เกล็นแจ็

@glenn: นี่เป็นความคิดแรกของฉัน แต่เมื่อฉันอ่านคู่มือ 'read' ฉันเห็นฟังก์ชันอาร์เรย์ที่ฉันพบว่ามีประโยชน์ นอกจากนี้ยังสามารถแก้ไขได้อย่างง่ายดายเพื่อให้มีการจัดทำดัชนีฟิลด์จากด้านขวา
jfg956

2
bashดัชนีอาเรย์เป็นเรื่องของการประเมินผลทางคณิตศาสตร์ดังนั้นecho ${line[nb - 1]}ก็เพียงพอแล้ว ในฐานะที่พูดเกี่ยวกับbashคุณก็สามารถข้าม“nb” echo ${line[-1]}สิ่ง: ทางเลือกที่พกพาได้มากกว่าในภายหลัง: echo ${line[@]: -1]}. (ดูความคิดเห็นของ Stephane Chazelas เกี่ยวกับดัชนีลบที่อื่น)
จัดการ

53

ลอง:

echo -e 'one two three\nfour five six\nseven eight nine' | awk '{print $NF}'

ฉันอัพเดท Q
LanceBaynes

โปรดทราบว่า awk ถูก จำกัด ที่ 99 เขตข้อมูล ... : / นี่เพิ่งกัดฉันในวันที่ผ่านมา ( ps -ef | awk '{ print $NF }'มีบางบรรทัดที่ถูกตัดทอน ... ) Perl ไม่มีข้อ จำกัด ที่ ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : "Awk ดั้งเดิมมีขีด จำกัด 99 ฟิลด์ในเร็กคอร์ดเนื่องจากการใช้งาน Awk บางอย่างเช่นของ Tru64 แยกอินพุตแม้ว่าคุณจะไม่ได้อ้างอิง ในฟิลด์ใด ๆ ในสคริปต์เพื่อหลีกเลี่ยงปัญหานี้ให้ตั้งค่า 'FS' เป็นอักขระที่ผิดปกติและใช้การแบ่ง ")
Olivier Dulac

@OlivierDulac awkการใช้งานมีข้อ จำกัด อย่างไรบ้าง ฉันไม่เคยเห็นมัน ฉันmawkจะสำลัก32768แต่ฉันgawkและigawkสามารถจัดการกับล้านอย่างมีความสุข แม้แต่ busybox ของฉันก็awkสามารถจัดการกับคนนับล้านได้ ฉันไม่เคยเจอกับawkสิ่งที่ไม่สามารถจัดการกับ 100 สาขานั่นคือตัวเลขเล็ก ๆ หลังจากทั้งหมด คุณแน่ใจหรือว่าข้อมูลยังมีความเกี่ยวข้อง แม้แต่บน Solaris
terdon

@terdon ดูลิงก์ในความคิดเห็นของฉัน ^^ (และเชื่อฉันระบบ "legacy" บางตัวสามารถเอาชีวิตรอด loooooooog ได้ในบางสภาพแวดล้อมในบางครั้ง tar tar แยกเป็น "/" bash ไม่มีประโยชน์อะไรเลย builtins (หรือ $ BASH_SOURCE เป็นต้น) awk choke บน NF> 99 ฯลฯ ... :()
Olivier Dulac

@ OlivierDulac ยุติธรรมพอสมควร ฉันแค่ไม่ได้เจอมัน ฉันหวังว่ามันจะหายไปได้ยากในวันนี้เนื่องจาก 99 มีจำนวนน้อย
terdon


11

ลองgrep(สั้นกว่า / เรียบง่าย แต่ช้ากว่า 3x awkเนื่องจากการใช้ regex):

grep -o '\S\+$' <(echo -e '... seven eight nine')

หรือex(ยิ่งช้าลง แต่พิมพ์บัฟเฟอร์ทั้งหมดหลังจากเสร็จสิ้นจะมีประโยชน์มากขึ้นเมื่อต้องเรียงลำดับหรือแก้ไขแบบแทนที่):

ex -s +'%s/^.*\s//g' -c'%p|q!' <(echo -e '... seven eight nine')
ex +'%norm $Bd0' -sc'%p|q!' infile

การเปลี่ยนในสถานที่แทนที่ด้วย-sc'%p|q!'-scwq

หรือbash:

while read line; do arr=($line); echo ${arr[-1]}; done < someinput

ประสิทธิภาพ

รับไฟล์ 1GB ที่สร้างขึ้นผ่าน:

$ hexdump -C /dev/urandom | rev | head -c1G | pv > datafile

ฉันใช้สถิติการแยกวิเคราะห์เวลา (วิ่ง ~ 3x และทดสอบขั้นต่ำที่สุดบน MBP OS X):

  • ใช้awk:

    $ time awk '{print $NF}' datafile > /dev/null
    real    0m12.124s
    user    0m10.704s
    sys 0m0.709s
  • ใช้grep:

    $ time grep -o '\S\+$' datafile > /dev/null
    real    0m36.731s
    user    0m36.244s
    sys 0m0.401s
    
    $ time grep -o '\S*$' datafile > /dev/null
    real    0m40.865s
    user    0m39.756s
    sys 0m0.415s
  • ใช้perl:

    $ time perl -lane 'print $F[-1]' datafile > /dev/null
    real    0m48.292s
    user    0m47.601s
    sys 0m0.396s
  • ใช้rev+ cut:

    $ time (rev|cut -d' ' -f1|rev) < datafile > /dev/null
    $ time rev datafile | cut -d' ' -f1 | rev > /dev/null
    real    1m10.342s
    user    1m19.940s
    sys 0m1.263s
  • ใช้ex:

    $ time ex +'%norm $Bd0_' -sc'%p|q!' datafile > /dev/null
    real    3m47.332s
    user    3m42.037s
    sys 0m2.617s
    $ time ex +'%norm $Bd0' -sc'%p|q!' datafile > /dev/null
    real    4m1.527s
    user    3m44.219s
    sys 0m6.164s
    $ time ex +'%s/^.*\s//g' -sc'%p|q!' datafile > /dev/null
    real    4m16.717s
    user    4m5.334s
    sys 0m5.076s
  • ใช้bash:

    $ time while read line; do arr=($line); echo ${arr[-1]}; done < datafile > /dev/null
    real    9m42.807s
    user    8m12.553s
    sys 1m1.955s

6
... | perl -lane 'print $F[-1]'

จุดสำคัญ: -a, เขตข้อมูลอัตโนมัติใน@Fอาร์เรย์; -lสับออก$/(ตัวคั่นเร็กคอร์ดอินพุต) และบันทึกลงใน$\(ตัวคั่นเร็กคอร์ดเอาต์พุต) เนื่องจากไม่มีเลขแปดที่มาพร้อมกับ-lต้นฉบับ$/จึงนำไปใช้ในการพิมพ์ (การสิ้นสุดบรรทัด) -nรหัสห่วง -eรันรหัสทันทีหลังจากนี้ man perlrunดู
Jonathan Komar

5

มันสามารถทำได้โดยใช้'sed':

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* \([^ ]*\)$/\1/'

ปรับปรุง:

หรือมากกว่านั้น:

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* //'

ง่ายกว่าดีกว่า!
mdpc

@mdpc: ฉันเห็นด้วย แต่เมื่อมีการแก้ปัญหาที่ซับซ้อนมากขึ้นแล้วฉันก็ตัดสินใจที่จะเก็บมันไว้
jfg956

5

หรือใช้cut:

echo -e 'one two three\nfour five six\nseven eight nine' | cut -f 3 -d' '

แม้ว่าสิ่งนี้จะไม่เป็นไปตามข้อกำหนด 'การแก้ปัญหาทั่วไป' ใช้revสองครั้งเราสามารถแก้ปัญหานี้เช่นกัน:

echo -e 'one two three\nfour five six\nseven eight nine' | rev | cut -f 1 -d' ' | rev

ฉันไม่คิดว่า 'rev' สามารถพบได้ใน Unix ทั้งหมด (AIX, Solaris, ... ) หรือติดตั้งบน Linux ทั้งหมด แต่เป็นทางเลือกที่ดี
jfg956

1
+1 สำหรับการหมุนซ้ำสองครั้ง แต่ในฐานะโน้ตข้างrevไม่สามารถใช้กับอักขระ 'wide' ได้เพียงไบต์เดียวเท่าที่ฉันรู้
Marcin

2

การใช้awkคุณสามารถตรวจสอบก่อนว่ามีอย่างน้อยหนึ่งคอลัมน์

echo | awk '{if (NF >= 1) print $NF}'

echo 1 2 3 | awk '{if (NF >= 1) print $NF}'

3
หรือน้อยกว่า awk 'NF{print $NF}'verbosely
จัดการ

1

ใน Perl นี้สามารถทำได้ดังนี้

#!/usr/bin/perl

#create a line of arbitrary data
$line = "1 2 3 4 5";

# splt the line into an array (we call the array 'array', for lolz)
@array = split(' ', $line);

# print the last element in the array, followed by a newline character;
print "$array[-1]\n";

เอาท์พุท:

$ perl last.pl
5
$

นอกจากนี้คุณยังสามารถวนซ้ำไฟล์เป็นสคริปต์ตัวอย่างที่ฉันเขียนเพื่อแยกไฟล์ที่เรียกว่า budget.dat

ข้อมูลตัวอย่างใน budget.dat:

Rent              500
Food              250
Car               300
Tax               100
Car Tax           120
Mag Subscription  15

(คุณสามารถเห็นฉันต้องการจับเฉพาะคอลัมน์ "สุดท้าย" ไม่ใช่คอลัมน์ 2 เท่านั้น)

บท:

#!/usr/bin/perl
$budgetfile = "budget.dat";
open($bf, $budgetfile)
        or die "Could not open filename: $filename $!";


print "-" x 50, "\n";
while ( $row = <$bf> ) {
        chomp $row;
        @r = split (' ', $row);
        print "$row ";
        $subtotal += $r[-1];
        print "\t$subtotal\n";
}
print "-" x 50, "\n";
print "\t\t\t Total:\t$subtotal\n\n";

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