ฉันจะพิมพ์บรรทัดจากไฟล์ย้อนหลังได้อย่างไร (โดยไม่ใช้“ tac”)


26

ฉันต้องการพิมพ์บรรทัดจากไฟล์ข้างหลังโดยไม่ใช้tacคำสั่ง มีวิธีอื่นในการทำสิ่งนั้นด้วยการทุบตี?


คำตอบ:


33

ใช้sedเพื่อเลียนแบบtac:

sed '1!G;h;$!d' ${inputfile}

3
นี่เป็นทางออกที่ดี แต่คำอธิบายบางอย่างเกี่ยวกับวิธีการทำงานนี้จะดีขึ้น
เฉินประกาศ

10
มันเป็นsedหนึ่งซับที่มีชื่อเสียง ดูที่ "36. เรียงลำดับบรรทัดย้อนกลับ (เลียนแบบคำสั่ง" tac "Unix) ในเซเลบที่มีชื่อเสียงหนึ่งเดียวอธิบายสำหรับคำอธิบายที่สมบูรณ์เกี่ยวกับวิธีการทำงาน
Johnsyweb

6
อาจจะน่าสังเกตว่า: "สอง liners สองตัวนี้ใช้หน่วยความจำจำนวนมากจริง ๆ เพราะพวกเขาเก็บไฟล์ทั้งหมดไว้ในบัฟเฟอร์ย้อนกลับก่อนที่จะพิมพ์ออกมาหลีกเลี่ยง one-liners เหล่านี้สำหรับไฟล์ขนาดใหญ่"
Mr. Shickadance

ดังนั้นคำตอบอื่น ๆ ทั้งหมด (ยกเว้นอาจใช้คำตอบsort- มีโอกาสที่มันจะใช้ไฟล์ชั่วคราว)
Random832

1
โปรดทราบว่าแทคจะเร็วกว่าสำหรับไฟล์ปกติเพราะจะอ่านไฟล์ย้อนกลับ สำหรับไพพ์ก็ต้องทำเช่นเดียวกับโซลูชั่นอื่น ๆ (เก็บไว้ในหน่วยความจำหรือในไฟล์ temp) ดังนั้นจึงไม่เร็วกว่าอย่างมาก
Stéphane Chazelas


6

awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file.txt

ผ่านawk หนึ่งสมุทร


4
อันที่สั้นกว่า:awk 'a=$0RS a{}END{printf a}'
ninjalj

@ninjalj: มันอาจจะสั้นกว่า แต่มันก็ช้ามากเมื่อขนาดไฟล์ใหญ่ขึ้น ฉันยอมแพ้หลังจากรอ 2 นาที 30 วินาที but your first perl reverse <> `คำตอบที่ดีที่สุด / เร็วที่สุดในหน้าเว็บ (ถึงฉัน) เร็วกว่าawkคำตอบนี้ถึง 10 เท่า(ผู้สังเกตการณ์ที่น่ากลัวทุกคนมีความเหมือนกันเวลาฉลาด)
Peter.O

1
หรือawk '{a[NR]=$0} END {while (NR) print a[NR--]}'
Stéphane Chazelas

5

ตามที่คุณขอให้ทำด้วยวิธีทุบตีนี่เป็นวิธีแก้ปัญหาที่ไม่ได้ใช้ awk, sed หรือ perl เพียงแค่ฟังก์ชั่นทุบตี:

reverse ()
{
    local line
    if IFS= read -r line
    then
        reverse
        printf '%s\n' "$line"
    fi
}

ผลลัพธ์ของ

echo 'a
b
c
d
' | reverse

คือ

d
c
b
a

อย่างที่คาดไว้.

แต่ระวังว่าบรรทัดจะถูกเก็บไว้ในหน่วยความจำหนึ่งบรรทัดในแต่ละครั้งที่เรียกว่าอินสแตนซ์ของฟังก์ชั่นซ้ำ ระมัดระวังด้วยไฟล์ขนาดใหญ่


1
มันช้าลงอย่างช้าๆเมื่อขนาดไฟล์เพิ่มขึ้นเมื่อเทียบกับคำตอบอื่น ๆ ที่ช้าที่สุดและอย่างที่คุณแนะนำมันทำให้หน่วยความจำสวยได้อย่างง่ายดาย: * ฟังก์ชั่น burs recursive ... แต่มันเป็นแนวคิดที่น่าสนใจ การแบ่งส่วนความผิดพลาด *
Peter.O

4

คุณสามารถส่งผ่าน:

awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

awkคำนำหน้าแต่ละบรรทัดที่มีจำนวนเส้นตามด้วยช่องว่าง sortฝืนคำสั่งของสายโดยเรียงลำดับในฟิลด์แรก (หมายเลขบรรทัด) เพื่อย้อนกลับสไตล์ที่เป็นตัวเลข และsedแถบปิดหมายเลขบรรทัด

ตัวอย่างต่อไปนี้แสดงสิ่งนี้ในการดำเนินการ:

pax$ echo 'a
b
c
d
e
f
g
h
i
j
k
l' | awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

มันเอาท์พุท:

l
k
j
i
h
g
f
e
d
c
b
a

ตกลง. ขอบคุณ! ฉันจะลองสิ่งนี้ และขอขอบคุณสำหรับความคิดเห็น!

5
cat -nทำหน้าที่เหมือนawk '{print NR" "$0}'
เฉินเลวี

2
ฉันคิดว่าที่เรียกว่าSchwartzian transformหรือตกแต่งเรียงลำดับ - ตกแต่ง
ninjalj

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

1

ใน Perl:

cat <somefile> | perl -e 'while(<>) { push @a, $_; } foreach (reverse(@a)) { print; }'

2
หรือสั้นกว่าperl -e 'print reverse<>'
ninjalj

2
(อันที่จริงมีสั้นหนึ่ง แต่มันเป็นเรื่องจริงที่น่าเกลียดพยานความน่ากลัวของมันperl -pe '$\=$_.$\}{')
ninjalj

@Frederik Deweerdt: เร็ว แต่มันเสียบรรทัดแรก ... @ ninjalj: reverse<)เร็วมาก: ดี! แต่อันที่ "น่าเกลียดจริงๆ" นั้นช้ามากเมื่อจำนวนบรรทัดเพิ่มขึ้น !! ....
Peter.O

@ Peter.O -nฟุ่มเฟือยที่นั่นขอบคุณ
Frederik Deweerdt

สิ่งที่ดีเกี่ยวกับสิ่งนี้คือเนื้อหาของไฟล์นั้นไม่จำเป็นต้องอ่านในหน่วยความจำ (ยกเว้นในหน่วยย่อย ๆsort)
Kusalananda

0

วิธีการแก้ปัญหา BASH เท่านั้น

อ่านไฟล์ลงใน bash array (หนึ่งบรรทัด = หนึ่งองค์ประกอบของอาร์เรย์) และพิมพ์อาร์เรย์ในลำดับย้อนกลับ:

i=0 

while read line[$i] ; do
    i=$(($i+1))
done < FILE


for (( i=${#line[@]}-1 ; i>=0 ; i-- )) ; do
    echo ${line[$i]}
done

ลองมันมีเส้นเยื้อง ... รุ่น philfr เป็นดีขึ้นเล็กน้อย แต่ยังคง veeeery slooooow while..readดังนั้นจริงๆเมื่อมันมาถึงการประมวลผลข้อความที่ไม่เคยใช้งาน
don_crissti

ใช้IFS=''และread -rเพื่อป้องกันการหลบหนีทุกชนิดและการกำจัด IFS ที่ตามมาไม่ให้คลาดเคลื่อน ฉันคิดว่า bash mapfile ARRAY_NAMEbuiltin เป็นวิธีแก้ปัญหาที่ดีกว่าสำหรับการอ่านอาร์เรย์
Arthur2e5

0

Bash พร้อมกับmapfileกล่าวถึงในความคิดเห็นเพื่อ fiximan และจริง ๆ แล้วอาจเป็นรุ่นที่ดีกว่า:

# last [LINES=50]
_last_flush(){ BUF=("${BUF[@]:$(($1-LINES)):$1}"); } # flush the lines, can be slow.
last(){
  local LINES="${1:-10}" BUF
  ((LINES)) || return 2
  mapfile -C _last_flush -c $(( (LINES<5000) ? 5000 : LINES+5 )) BUF
  BUF=("${BUF[@]}") # Make sure the array subscripts make sence, can be slow.
  ((LINES="${#BUF[@]}" > LINES ? LINES : "${#BUF[@]}"))
  for ((i="${#BUF[@]}"; i>"${#BUF[@]}"-LINES; i--)); do echo -n "${BUF[i]}"; done
}

ประสิทธิภาพของมันนั้นเทียบเท่ากับsedวิธีแก้ปัญหาและเร็วขึ้นเมื่อจำนวนบรรทัดที่ร้องขอลดลง



0
  • หมายเลขบรรทัดด้วย nl
  • เรียงลำดับย้อนกลับตามหมายเลข
  • ลบตัวเลขด้วย sed

ตามที่ปรากฏที่นี่:

echo 'e
> f
> a
> c
> ' | nl | sort -nr | sed -r 's/^ *[0-9]+\t//'

ผล:

c
a
f
e

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