Bagaimana saya bisa mencetak baris dari file mundur (tanpa menggunakan "tac")?


Jawaban:


33

Menggunakan seduntuk meniru tac:

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

3
Ini adalah solusi yang baik, tetapi beberapa penjelasan tentang cara kerjanya akan lebih baik.
Chen Levy

10
Ini adalah sedone-liner yang terkenal . Lihat "36. Membalik urutan baris (meniru" tac "perintah Unix)." dalam Famous Sed One-Liners Dijelaskan untuk penjelasan lengkap tentang cara kerjanya.
Johnsyweb

6
Mungkin perlu dicatat: "Dua one-liners ini sebenarnya menggunakan banyak memori karena mereka menyimpan seluruh file dalam buffer yang ditahan dalam urutan terbalik sebelum mencetaknya. Hindari satu-liner ini untuk file besar."
Tn. Shickadance

Jadi lakukan semua jawaban lain (kecuali mungkin yang menggunakan sort- ada kemungkinan itu akan menggunakan file sementara).
Random832

1
Perhatikan bahwa tac lebih cepat untuk file biasa karena membaca file mundur. Untuk pipa, ia harus melakukan hal yang sama dengan solusi lain (tahan dalam memori atau dalam file temp), jadi tidak jauh lebih cepat.
Stéphane Chazelas

8

Dengan ed:

ed -s infile <<IN
g/^/m0
,p
q
IN

Jika Anda menggunakan BSD/ OSX(dan semoga segera di GNU/ linuxjuga karena POSIX ):

tail -r infile

6

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

via awk satu liners


4
Yang lebih pendek:awk 'a=$0RS a{}END{printf a}'
ninjalj

@ninjalj: mungkin lebih pendek, tetapi menjadi sangat lambat karena ukuran file semakin besar. Saya menyerah setelah menunggu 2 menit 30 detik. but your first perl membalikkan <> `itu jawaban terbaik / tercepat pada halaman (untuk saya), pada 10 kali lebih cepat dari awkjawaban ini (semua awk anseres hampir sama,
hemat

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

5

Saat Anda diminta untuk melakukannya di bash, berikut adalah solusi yang tidak menggunakan awk, sed atau perl, hanya fungsi bash:

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

Output dari

echo 'a
b
c
d
' | reverse

aku s

d
c
b
a

Seperti yang diharapkan.

Tetapi berhati-hatilah bahwa garis-garis disimpan dalam memori, satu baris di masing-masing secara rekursif disebut instance dari fungsi. Jadi hati-hati dengan file besar.


1
Dengan cepat menjadi lambat karena ukuran file meningkat, dibandingkan dengan yang paling lambat dari jawaban lainnya, dan seperti yang telah Anda sarankan, ia menghancurkan memori dengan cukup mudah: * bash fungsi rekursif ... tapi ini ide yang menarik. Kesalahan segmentasi *
Peter.O

4

Anda dapat menyalurkan melalui:

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

The awkprefiks setiap baris dengan nomor baris diikuti dengan spasi. The sortmembalikkan urutan garis dengan mensortir field pertama (nomor baris) dalam urutan terbalik, gaya numerik. Dan sedstrip dari nomor baris.

Contoh berikut menunjukkan ini dalam tindakan:

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'

Ini menghasilkan:

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

Baik. Terima kasih! Saya akan coba ini. Dan terima kasih atas komentarnya!

5
cat -nbertindak sepertiawk '{print NR" "$0}'
Chen Levy

2
Saya pikir itu disebut transformasi Schwartzian , atau menghias-semacam-undecorate
ninjalj

Pendekatan umum ini bagus dalam menangani semacam itu menggunakan file pada disk jika tugas terlalu besar untuk dilakukan dalam memori. Mungkin lebih lembut di memori jika Anda menggunakan file sementara di antara langkah-langkah daripada pipa.
mc0e

1

Dalam perl:

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

2
atau yang lebih pendekperl -e 'print reverse<>'
ninjalj

2
(Sebenarnya, ada yang lebih pendek, tapi itu benar-benar jelek, saksi awfulness nya: perl -pe '$\=$_.$\}{')
ninjalj

@Frederik Deweerdt: Cepat, tetapi ia kehilangan baris pertama ... @ ninjalj: reverse<)cepat: bagus! tetapi yang "benar-benar jelek" sangat lambat karena jumlah garis meningkat. !! ....
Peter.O

@ Peter.O -nitu berlebihan di sana, terima kasih.
Frederik Deweerdt

Yang menyenangkan tentang yang satu ini adalah bahwa isi file belum tentu dibaca ke dalam memori (kecuali mungkin dalam potongan oleh sort).
Kusalananda

0

Solusi khusus BASH

baca file menjadi bash array (satu baris = satu elemen array) dan cetak array dalam urutan terbalik:

i=0 

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


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

Cobalah dengan baris indentasi ... versi philfr sedikit lebih baik tetapi masih sangat slooooow jadi, ketika berbicara tentang pemrosesan teks, jangan pernah gunakan while..read.
don_crissti

Gunakan IFS=''dan read -runtuk mencegah semua jenis lolos dan tertinggal penghapusan IFS dari mengacaukannya. Saya pikir bash mapfile ARRAY_NAMEbuiltin adalah solusi yang lebih baik untuk membaca array.
Arthur2e5

0

Bash, dengan mapfiledisebutkan dalam komentar untuk fiximan, dan sebenarnya versi yang mungkin lebih baik:

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

Kinerjanya pada dasarnya sebanding dengan sedsolusi, dan semakin cepat karena jumlah baris yang diminta berkurang.


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

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


0
  • Beri nomor baris dengan nl
  • urutkan dalam urutan terbalik dengan nomor
  • hapus angka dengan sed

seperti yang ditunjukkan di sini:

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

Hasil:

c
a
f
e

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.