Làm cách nào tôi có thể in các dòng từ tập tin ngược (mà không cần sử dụng tính năng của hệ thống)?


Câu trả lời:


33

Sử dụng sedđể mô phỏng tac:

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

3
Đây là một giải pháp tốt, nhưng một số giải thích cho cách thức hoạt động này sẽ còn tốt hơn nữa.
Chen Levy

10
Đó là một sedlớp lót nổi tiếng . Xem "36. Thứ tự đảo ngược của dòng (mô phỏng lệnh" tac "Unix)." trong Nổi tiếng Sed One-Liners Giải thích cho một lời giải thích đầy đủ về cách thức hoạt động của nó.
Johnsyweb

6
Có lẽ đáng chú ý: "Hai lớp lót này thực sự sử dụng rất nhiều bộ nhớ vì chúng giữ toàn bộ tệp trong bộ đệm theo thứ tự ngược lại trước khi in ra. Tránh các lớp lót này cho các tệp lớn."
Ông Shickadance 17/03/2016

Vì vậy, làm tất cả các câu trả lời khác (ngoại trừ có thể là câu trả lời sử dụng sort- có khả năng nó sẽ sử dụng một tệp tạm thời).
Random832

1
Lưu ý rằng tac nhanh hơn cho các tệp thông thường vì nó đọc tệp ngược. Đối với đường ống, nó phải thực hiện giống như các giải pháp khác (giữ trong bộ nhớ hoặc trong tệp tạm thời), do đó không nhanh hơn đáng kể.
Stéphane Chazelas


6

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

thông qua awk một lót


4
Một cái ngắn hơn:awk 'a=$0RS a{}END{printf a}'
ninjalj

@ninjalj: nó có thể ngắn hơn, nhưng nó trở nên cực kỳ chậm khi kích thước tệp lớn hơn. Tôi đã bỏ cuộc sau khi chờ đợi 2 phút 30 giây. but your first Perl đảo ngược <> `nó là câu trả lời tốt nhất / nhanh nhất trên trang (với tôi), lúc 10 lần nhanh hơn so với này awkcâu trả lời (tất cả các anseres awk là như nhau, thời gian khôn ngoan)
Peter.O

1
Hoặcawk '{a[NR]=$0} END {while (NR) print a[NR--]}'
Stéphane Chazelas

5

Như bạn đã yêu cầu làm điều đó trong bash, đây là một giải pháp không sử dụng awk, sed hoặc perl, chỉ là một hàm bash:

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

Đầu ra của

echo 'a
b
c
d
' | reverse

d
c
b
a

Như mong đợi.

Nhưng hãy cẩn thận rằng các dòng được lưu trữ trong bộ nhớ, một dòng trong mỗi thể hiện được gọi đệ quy của hàm. Vì vậy, cẩn thận với các tập tin lớn.


1
Nó nhanh chóng trở nên chậm chính xác khi kích thước tệp tăng lên, so với mức chậm nhất trong các câu trả lời khác, và như bạn đã đề xuất, nó thổi bộ nhớ khá dễ dàng: * bash chức năng đệ quy ... nhưng đó là một ý tưởng thú vị. Lỗi phân đoạn *
Peter.O

4

Bạn có thể dẫn nó qua:

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

Các awktiền tố mỗi dòng với số dòng theo sau là khoảng trắng. Việc sortđảo ngược thứ tự của các dòng bằng cách sắp xếp trên trường đầu tiên (số dòng) theo thứ tự ngược lại, kiểu số. Và các seddải ra khỏi số dòng.

Ví dụ sau đây cho thấy điều này trong hành động:

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'

Nó xuất ra:

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

Được. Cảm ơn! Tôi sẽ thử cái này. Và cảm ơn các ý kiến!

5
cat -nhành động giống nhưawk '{print NR" "$0}'
Chen Levy

2
Tôi nghĩ đó được gọi là biến đổi Schwartzian , hoặc trang trí sắp xếp không trang trọng
ninjalj 17/03/2016

Cách tiếp cận chung này là tốt ở chỗ sắp xếp xử lý bằng cách sử dụng các tệp trên đĩa nếu tác vụ quá lớn để thực hiện hợp lý trong bộ nhớ. Nó có thể nhẹ nhàng hơn trên bộ nhớ mặc dù nếu bạn sử dụng các tệp tạm thời giữa các bước thay vì đường ống.
mc0e

1

Trong perl:

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

2
hoặc ngắn hơnperl -e 'print reverse<>'
ninjalj 17/03/2016

2
(Trên thực tế, có một ngắn một, nhưng nó thực sự xấu xí, chứng khủng khiếp của nó: perl -pe '$\=$_.$\}{')
ninjalj

@Frederik Deweerdt: Nhanh, nhưng mất dòng đầu tiên ... @ ninjalj: reverse<)là nhanh: tốt! nhưng cái "thực sự xấu xí" thì cực kỳ chậm khi số dòng tăng lên. !! ....
Peter.O

@ Peter.O -nlà không cần thiết ở đó, cảm ơn.
Frederik Deweerdt

Điều thú vị về điều này là nội dung của tệp không nhất thiết phải được đọc vào bộ nhớ (ngoại trừ có thể trong các đoạn của sort).
Kusalananda

0

Giải pháp chỉ BASH

đọc tệp vào mảng bash (một dòng = một phần tử của mảng) và in ra mảng theo thứ tự ngược lại:

i=0 

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


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

Hãy thử nó với các dòng thụt lề ... phiên bản của philfr tốt hơn một chút nhưng vẫn rất dễ hiểu, thực sự, khi nói đến xử lý văn bản, không bao giờ sử dụng while..read.
don_crissti

Sử dụng IFS=''read -rđể ngăn chặn tất cả các loại thoát và theo dõi loại bỏ IFS khỏi làm hỏng nó. Tôi nghĩ rằng bash mapfile ARRAY_NAMEdựng sẵn là một giải pháp tốt hơn để đọc vào mảng mặc dù.
Arthur2e5

0

Bash, với các mapfileý kiến ​​được đề cập đến fiximan, và thực sự là một phiên bản có thể tốt hơn:

# 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
}

Hiệu suất của nó về cơ bản có thể so sánh với sedgiải pháp và nhanh hơn khi số lượng dòng được yêu cầu giảm.


0
Simple do this with your file to sort the data in reverse order, and it should be unique.

sed -n '1h; 1d; G; h; $ p'


0
  • Đánh số các dòng bằng nl
  • sắp xếp theo thứ tự ngược theo số
  • loại bỏ các số với sed

như được hiển thị ở đây:

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

Kết quả:

c
a
f
e

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.