seenadalah asosiatif-array yang Awk akan melewati setiap baris file. Jika suatu baris tidak ada dalam array maka seen[$0]akan bernilai false. Ini !adalah operator TIDAK logis dan akan membalikkan false ke true. Awk akan mencetak garis di mana ekspresi bernilai true. The ++bertahap seensehingga seen[$0] == 1setelah pertama kali garis ditemukan dan kemudian seen[$0] == 2, dan sebagainya.
Awk mengevaluasi segalanya kecuali 0dan ""(string kosong) menjadi true. Jika garis duplikat ditempatkan di seenkemudian !seen[$0]akan mengevaluasi ke false dan garis tidak akan ditulis ke output.
Untuk menyimpannya dalam file, kita bisa melakukan iniawk '!seen[$0]++' merge_all.txt > output.txt
Akash Kandpal
5
Peringatan penting di sini: jika Anda perlu melakukan ini untuk banyak file, dan Anda menangani lebih banyak file di akhir perintah, atau menggunakan wildcard ... larik 'terlihat' akan diisi dengan garis duplikat dari SEMUA file. Jika Anda ingin memperlakukan setiap file secara independen, Anda harus melakukan sesuatu sepertifor f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Nick K9
@ NickK9 bahwa de-duping secara kumulatif di banyak file itu sendiri mengagumkan. Tip bagus
# delete duplicate, consecutive lines from a file (emulates "uniq").# First line in a set of duplicate lines is kept, rest are deleted.
sed '$!N; /^\(.*\)\n\1$/!P; D'# delete duplicate, nonconsecutive lines from a file. Beware not to# overflow the buffer size of the hold space, or else use GNU sed.
sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'
geekery ;-) +1, tetapi konsumsi sumber daya tidak dapat dihindari.
Michael Krelin - hacker
3
'$! N; /^(.*)1n1/1/!P; D 'berarti "Jika Anda tidak berada di baris terakhir, baca di baris lain. Sekarang lihat apa yang Anda miliki dan jika itu TIDAK diikuti oleh baris baru dan kemudian hal yang sama lagi, cetak barang-barang itu. Sekarang hapus barang (hingga baris baru). "
Beta
2
'G; s / \ n / && /; / ^ ([- ~] * \ n). * \ n \ 1 / d; s / \ n //; h; P 'berarti, secara kasar, "Tambahkan seluruh ruang pegang baris ini, maka jika Anda melihat garis duplikat membuang semuanya, jika tidak, salin seluruh kekacauan kembali ke ruang pegang dan cetak bagian pertama (yang merupakan baris yang baru saja Anda buat baca. "
Beta
Apakah $!bagian itu perlu? Tidak sed 'N; /^\(.*\)\n\1$/!P; D'melakukan hal yang sama? Saya tidak dapat memberikan contoh di mana keduanya berbeda pada mesin saya (fwiw saya memang mencoba baris kosong di akhir dengan kedua versi dan keduanya baik-baik saja).
eddi
1
Hampir 7 tahun kemudian dan tidak ada yang menjawab @amichair ... <sniff> membuat saya sedih. ;) Bagaimanapun, [ -~]mewakili berbagai karakter ASCII dari 0x20 (spasi) hingga 0x7E (tilde). Ini dianggap sebagai karakter ASCII yang dapat dicetak (halaman yang ditautkan juga memiliki 0x7F / hapus tetapi sepertinya tidak benar). Itu membuat solusi rusak bagi siapa pun yang tidak menggunakan ASCII atau siapa pun yang menggunakan, katakanlah, karakter tab .. Semakin portabel [^\n]mencakup lebih banyak karakter ... semua dari mereka kecuali satu, pada kenyataannya.
B Layer
14
Perl one-liner mirip dengan solusi awk @ jonas:
perl -ne 'print if ! $x{$_}++' file
Variasi ini menghilangkan spasi spasi sebelum membandingkan:
perl -lne 's/\s*$//; print if ! $x{$_}++' file
Variasi ini mengedit file di tempat:
perl -i -ne 'print if ! $x{$_}++' file
Variasi ini mengedit file di tempat, dan membuat cadangan file.bak
Satu-liner yang diposting Andre Miller di atas berfungsi kecuali untuk versi sed terbaru ketika file input berakhir dengan baris kosong dan tanpa karakter. Di Mac saya, CPU saya hanya berputar.
Infinite loop jika baris terakhir kosong dan tidak memiliki karakter :
sed '$!N; /^\(.*\)\n\1$/!P; D'
Tidak menggantung, tetapi Anda kehilangan baris terakhir
sed '$d;N; /^\(.*\)\n\1$/!P; D'
Penjelasannya ada di bagian paling akhir dari FAQ sed :
Pemelihara GNU merasa bahwa terlepas dari masalah portabilitas
ini akan menyebabkan, mengubah perintah N untuk mencetak (daripada
menghapus) ruang pola lebih konsisten dengan intuisi seseorang
tentang bagaimana perintah untuk "menambahkan baris berikutnya" harus berperilaku.
Fakta lain yang mendukung perubahan adalah bahwa "{N; command;}" akan
menghapus baris terakhir jika file memiliki jumlah ganjil baris, tetapi
mencetak baris terakhir jika file memiliki jumlah garis genap.
Untuk mengonversi skrip yang menggunakan perilaku N sebelumnya (menghapus
ruang pola setelah mencapai EOF) menjadi skrip yang kompatibel dengan
semua versi sed, ubah "N;" ke "$ d; N;" .
print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.
Menjelaskan:
$!N;: jika baris saat ini BUKAN baris terakhir, gunakan Nperintah untuk membaca baris selanjutnyapattern space .
/^(.*)\n\1$/!P: jika konten saat ini pattern spaceadalah dua duplicate stringdipisahkan oleh \n, yang berarti baris berikutnya adalah samedengan baris saat ini, kami TIDAK dapat mencetaknya sesuai dengan ide inti kami; jika tidak, yang berarti baris saat ini adalah penampilan TERAKHIR dari semua baris duplikat berturut-turut, sekarang kita dapat menggunakanP perintah untuk mencetak karakter dalam pattern spaceutil saat ini \n(\n juga dicetak).
D: kami menggunakan Dperintah untuk menghapus karakter saat inipattern space util\n ( \njuga dihapus), kemudian kontenpattern space adalah baris berikutnya.
dan Dperintah akan memaksased untuk melompat ke FIRSTperintahnya $!N, tetapi TIDAK membaca baris berikutnya dari file atau aliran input standar.
Solusi kedua mudah dipahami (dari diri saya sendiri):
print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.
Menjelaskan:
baca baris baru dari aliran input atau file dan cetak sekali.
gunakan :loopperintah atur labelnamaloop .
gunakan Nuntuk membaca baris berikutnya ke dalampattern space .
gunakan s/^(.*)\n\1$/\1/untuk menghapus baris saat ini jika baris berikutnya sama dengan baris saat ini, kami menggunakan sperintah untuk melakukandelete tindakan.
jika sperintah dieksekusi dengan sukses, maka gunakan tloopkekuatan perintah seduntuk melompat ke labelnama loop, yang akan melakukan loop yang sama ke baris berikutnya menggunakan tidak ada duplikat baris berturut-turut dari garis yang latest printed; jika tidak, gunakan Dperintah ke deletebaris yang sama dengan latest-printed line, dan paksakan seduntuk melompat ke perintah pertama, yang merupakan pperintah, konten saat ini pattern spaceadalah baris baru berikutnya.
uniq
saja sudah cukup.awk
, tetapi akan memakan banyak sumber daya pada file yang lebih besar.Jawaban:
seen
adalah asosiatif-array yang Awk akan melewati setiap baris file. Jika suatu baris tidak ada dalam array makaseen[$0]
akan bernilai false. Ini!
adalah operator TIDAK logis dan akan membalikkan false ke true. Awk akan mencetak garis di mana ekspresi bernilai true. The++
bertahapseen
sehinggaseen[$0] == 1
setelah pertama kali garis ditemukan dan kemudianseen[$0] == 2
, dan sebagainya.Awk mengevaluasi segalanya kecuali
0
dan""
(string kosong) menjadi true. Jika garis duplikat ditempatkan diseen
kemudian!seen[$0]
akan mengevaluasi ke false dan garis tidak akan ditulis ke output.sumber
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Dari http://sed.sourceforge.net/sed1line.txt : (Tolong jangan tanya saya bagaimana ini bekerja ;-))
sumber
$!
bagian itu perlu? Tidaksed 'N; /^\(.*\)\n\1$/!P; D'
melakukan hal yang sama? Saya tidak dapat memberikan contoh di mana keduanya berbeda pada mesin saya (fwiw saya memang mencoba baris kosong di akhir dengan kedua versi dan keduanya baik-baik saja).[ -~]
mewakili berbagai karakter ASCII dari 0x20 (spasi) hingga 0x7E (tilde). Ini dianggap sebagai karakter ASCII yang dapat dicetak (halaman yang ditautkan juga memiliki 0x7F / hapus tetapi sepertinya tidak benar). Itu membuat solusi rusak bagi siapa pun yang tidak menggunakan ASCII atau siapa pun yang menggunakan, katakanlah, karakter tab .. Semakin portabel[^\n]
mencakup lebih banyak karakter ... semua dari mereka kecuali satu, pada kenyataannya.Perl one-liner mirip dengan solusi awk @ jonas:
Variasi ini menghilangkan spasi spasi sebelum membandingkan:
Variasi ini mengedit file di tempat:
Variasi ini mengedit file di tempat, dan membuat cadangan
file.bak
sumber
Satu-liner yang diposting Andre Miller di atas berfungsi kecuali untuk versi sed terbaru ketika file input berakhir dengan baris kosong dan tanpa karakter. Di Mac saya, CPU saya hanya berputar.
Infinite loop jika baris terakhir kosong dan tidak memiliki karakter :
sed '$!N; /^\(.*\)\n\1$/!P; D'
Tidak menggantung, tetapi Anda kehilangan baris terakhir
sed '$d;N; /^\(.*\)\n\1$/!P; D'
Penjelasannya ada di bagian paling akhir dari FAQ sed :
sumber
Cara alternatif menggunakan Vim (kompatibel Vi) :
Hapus duplikat, baris berturut-turut dari file:
vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq
Hapus duplikat, baris tidak berturut-turut dan kosong dari file:
vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq
sumber
Solusi pertama juga dari http://sed.sourceforge.net/sed1line.txt
ide intinya adalah:
Menjelaskan:
$!N;
: jika baris saat ini BUKAN baris terakhir, gunakanN
perintah untuk membaca baris selanjutnyapattern space
./^(.*)\n\1$/!P
: jika konten saat inipattern space
adalah duaduplicate string
dipisahkan oleh\n
, yang berarti baris berikutnya adalahsame
dengan baris saat ini, kami TIDAK dapat mencetaknya sesuai dengan ide inti kami; jika tidak, yang berarti baris saat ini adalah penampilan TERAKHIR dari semua baris duplikat berturut-turut, sekarang kita dapat menggunakanP
perintah untuk mencetak karakter dalampattern space
util saat ini\n
(\n
juga dicetak).D
: kami menggunakanD
perintah untuk menghapus karakter saat inipattern space
util\n
(\n
juga dihapus), kemudian kontenpattern space
adalah baris berikutnya.D
perintah akan memaksased
untuk melompat keFIRST
perintahnya$!N
, tetapi TIDAK membaca baris berikutnya dari file atau aliran input standar.Solusi kedua mudah dipahami (dari diri saya sendiri):
ide intinya adalah:
Menjelaskan:
:loop
perintah aturlabel
namaloop
.N
untuk membaca baris berikutnya ke dalampattern space
.s/^(.*)\n\1$/\1/
untuk menghapus baris saat ini jika baris berikutnya sama dengan baris saat ini, kami menggunakans
perintah untuk melakukandelete
tindakan.s
perintah dieksekusi dengan sukses, maka gunakantloop
kekuatan perintahsed
untuk melompat kelabel
namaloop
, yang akan melakukan loop yang sama ke baris berikutnya menggunakan tidak ada duplikat baris berturut-turut dari garis yanglatest printed
; jika tidak, gunakanD
perintah kedelete
baris yang sama denganlatest-printed line
, dan paksakansed
untuk melompat ke perintah pertama, yang merupakanp
perintah, konten saat inipattern space
adalah baris baru berikutnya.sumber
busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
Ini dapat dicapai menggunakan awk
Below Line akan menampilkan Nilai unik
Anda dapat menampilkan nilai unik ini ke file baru
file baru uniq_file_name hanya akan berisi nilai unik, tidak ada duplikat
sumber
Menghapus garis duplikat menggunakan awk.
sumber
cat
tidak berguna. Lagi pula,uniq
sudah melakukan ini dengan sendirinya, dan tidak memerlukan input tepat satu kata per baris.