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

26

Saya ingin mencetak baris dari file mundur tanpa menggunakan tacperintah. Apakah ada solusi lain untuk melakukan hal seperti itu dengan bash?

jimmij
sumber
stackoverflow.com/questions/742466/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Jawaban:

33

Menggunakan seduntuk meniru tac:

sed '1!G;h;$!d' ${inputfile}
Johnsyweb
sumber
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
don_crissti
sumber
6

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

via awk satu liners

Mark McKinstry
sumber
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.

philfr
sumber
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

sumber
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; }'
Frederik Deweerdt
sumber
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
Fiximan
sumber
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.

Arthur2e5
sumber
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'

Bipul kumar
sumber
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
Pengguna tidak diketahui
sumber
-3
awk '{print NR" "$0}' | sort -nr
Rockstar
sumber