Unix'te bir dosyadaki yinelenen satırları silmenin bir yolu var mı?
Bunu sort -u
ve uniq
komutlarıyla yapabilirim, ancak sed
veya kullanmak istiyorum awk
. Mümkün mü?
awk
, ancak daha büyük dosyalarda oldukça kaynak tüketecek.
Unix'te bir dosyadaki yinelenen satırları silmenin bir yolu var mı?
Bunu sort -u
ve uniq
komutlarıyla yapabilirim, ancak sed
veya kullanmak istiyorum awk
. Mümkün mü?
awk
, ancak daha büyük dosyalarda oldukça kaynak tüketecek.
Yanıtlar:
awk '!seen[$0]++' file.txt
seen
Awk'nin dosyanın her satırını ileteceği ilişkilendirilebilir bir dizidir. Dizide bir satır yoksa, seen[$0]
yanlış olarak değerlendirilir. !
Mantıksal DEĞİL operatörüdür ve true false tersine çevirir. Awk, ifadenin doğru olarak değerlendirildiği satırları yazdırır. ++
Artışlarla seen
böylece seen[$0] == 1
sonra ilk kez bir satır ve daha sonra bulunan seen[$0] == 2
, ve böyle devam eder.
Awk, 0
ve ""
(boş dize) dışındaki her şeyi doğru olarak değerlendirir. Yinelenen bir çizgi yerleştirilirse seen
o zaman !seen[$0]
false olarak değerlendirecektir ve çizgi çıktı yazılır edilmeyecektir.
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Gönderen http://sed.sourceforge.net/sed1line.txt : (nasıl bu işleri bana sormayın lütfen ;-))
# 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'
$!
kısım gerekli? sed 'N; /^\(.*\)\n\1$/!P; D'
Aynı şeyi yapmıyor mu ? Makinemde ikisinin farklı olduğu bir örnek bulamıyorum (fwiw her iki sürümün sonunda boş bir satır denedim ve her ikisi de iyi).
[ -~]
0x20 (boşluk) ile 0x7E (tilde) arasındaki bir ASCII karakter aralığını temsil eder. Bunlar kabul edilir yazdırılabilir ASCII karakterleri (bağlantılı sayfa ayrıca 0x7F / silme vardır ama bu doğru görünmüyor). Bu, ASCII kullanmayan veya sekme karakterleri kullanmayanlar için çözümü bozar. Daha taşınabilir olan çok daha fazla karakter içerir… aslında biri hariç hepsi. [^\n]
@ Jonas'ın awk çözümüne benzer Perl tek satırlık:
perl -ne 'print if ! $x{$_}++' file
Bu varyasyon, karşılaştırmadan önce sondaki beyaz boşluğu kaldırır:
perl -lne 's/\s*$//; print if ! $x{$_}++' file
Bu varyasyon, dosyayı yerinde düzenler:
perl -i -ne 'print if ! $x{$_}++' file
Bu varyasyon, dosyayı yerinde düzenler ve bir yedek oluşturur file.bak
perl -i.bak -ne 'print if ! $x{$_}++' file
Yukarıda Andre Miller'ın gönderdiği tek satırlık, giriş dosyası boş bir satırla ve karakter olmadan sona erdiğinde sed'in son sürümleri dışında çalışır. Mac'imde CPU'm sadece dönüyor.
Son satır boşsa ve karakter içermiyorsa sonsuz döngü :
sed '$!N; /^\(.*\)\n\1$/!P; D'
Takılmıyor ama son satırı kaybediyorsun
sed '$d;N; /^\(.*\)\n\1$/!P; D'
Açıklama sed SSS'nin en sonunda yer almaktadır :
GNU sed bakımcısı
, bunun neden olabileceği taşınabilirlik sorunlarına rağmen,
model alanını yazdırmak için ( silmek yerine ) N komutunu değiştirmenin,
"Sonraki satırı ekleme" komutunun nasıl davranması gerektiğine dair kişinin sezgileriyle daha tutarlı olduğunu hissetti .
Değişikliği destekleyen bir başka gerçek de, "{N; command;}"
dosyasında tek sayıda satır varsa son satırı silecek, ancak dosyanın çift sayıda
satırı varsa son satırı yazdıracak olmasıdır.N'nin önceki davranışını (EOF'ye
ulaştıktan sonra desen alanını silme) kullanan komut dosyalarını sed'in
tüm sürümleriyle uyumlu komut dosyalarına dönüştürmek için tek bir "N" değiştirin; "$ d; N;" .
$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr '$!N;/^(.*)\n\1$/!P;D'
1
2
3
4
5
ana fikir şudur:
print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.
açıklar:
$!N;
: mevcut satır son satır DEĞİLSE N
, sonraki satırı okumak için komutu kullanın pattern space
./^(.*)\n\1$/!P
: Akımının içeriğini eğer pattern space
iki duplicate string
ayrılmış \n
bir sonraki çizgi anlamına gelir, same
mevcut satır bizim temel fikrine göre, de Yazdıramıyor; aksi halde, geçerli satırı anlamına gelen onun yinelenen ardışık hatlarının tümünün SON görünümü, biz şimdi kullanabilirsiniz P
akım karakter yazdırmak için komutu pattern space
util \n
( \n
ayrıca yazdırılır).D
: Kullandığımız D
akım karakter silmek için komut pattern space
util \n
( \n
daha sonra içeriği de silindi) pattern space
sonraki çizgidir.D
komut zorlar sed
onun atlamak için FIRST
komuta $!N
ancak dosya veya standart girdi akışından sonraki satırını okumak DEĞİL.$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr 'p;:loop;$!N;s/^(.*)\n\1$/\1/;tloop;D'
1
2
3
4
5
ana fikir şudur:
print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.
açıklar:
:loop
komutu bir seti label
adında loop
.N
doğru bir sonraki satırını okumak için pattern space
.s/^(.*)\n\1$/\1/
mevcut satırı silmek için sonraki satır mevcut satırla aynıysa s
, delete
eylemi yapmak için komut kullanırız .s
komut başarılı bir şekilde çalıştırılırsa, o zaman adlandırılmışa atlamak için tloop
komut kuvvetini sed
kullanın , bu aynı döngüyü sonraki satırlara yapacak olan satırın ardışık yinelenen satırları yoktur ; aksi takdirde, satırla aynı olan komutu kullanın ve ilk komuta atlamaya zorlayın , bu komuttur, akımın içeriği sonraki yeni satırdır.label
loop
latest printed
D
delete
latest-printed line
sed
p
pattern space
busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
Awk kullanarak yinelenen satırları siler.
cat
uniq
uniq
, o zaman tek başına yeterlidir.