seenjest tablicą asocjacyjną, do której Awk przekaże każdą linię pliku. Jeśli linia nie znajduje się w tablicy, seen[$0]zostanie obliczona jako fałsz. !Jest operator logiczny NOT i odwrócić false na true. Awk wypisze wiersze, w których wyrażenie przyjmuje wartość true. Te ++przyrosty seentak, że seen[$0] == 1po raz pierwszy wiersz zostanie znaleziony, a następnie seen[$0] == 2, i tak dalej.
Awk ocenia wszystko oprócz 0i ""(pusty ciąg) na wartość true. Jeśli zostanie wstawiony zduplikowany wiersz, seenwówczas !seen[$0]zostanie obliczony jako fałsz i wiersz nie zostanie zapisany na wyjściu.
Aby zapisać go w pliku, możemy to zrobićawk '!seen[$0]++' merge_all.txt > output.txt
Akash Kandpal
5
Ważne ostrzeżenie: jeśli musisz to zrobić dla wielu plików i dodasz więcej plików na końcu polecenia lub użyjesz symbolu wieloznacznego… tablica „widziana” zapełni się zduplikowanymi wierszami ze WSZYSTKICH plików. Jeśli zamiast tego chcesz traktować każdy plik niezależnie, musisz zrobić coś takiegofor f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Nick K9,
@ NickK9, które skumulowane usuwanie dupleksu w wielu plikach jest niesamowite samo w sobie. Niezła wskazówka
# 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, ale zużycie zasobów jest nieuniknione.
Michael Krelin - haker
3
'$! N; /^(.*)\n\1$/!P; D 'oznacza "Jeśli nie jesteś w ostatnim wierszu, przeczytaj inny wiersz. Teraz spójrz na to, co masz i jeśli NIE JEST to rzecz, po której następuje nowa linia, a następnie ponownie to samo, wydrukuj to. Teraz usuń rzeczy (do nowej linii). "
Beta
2
'SOL; s / \ n / && /; / ^ ([- ~] * \ n). * \ n \ 1 / d; s / \ n //; h; P 'oznacza w przybliżeniu „Dołącz całą przestrzeń do przechowywania w tym wierszu, a następnie, jeśli zobaczysz zduplikowaną linię, wyrzuć całość, w przeciwnym razie skopiuj cały bałagan z powrotem do miejsca przechowywania i wydrukuj pierwszą część (która jest właśnie linią czytaj. ”
Beta
Czy ta $!część jest konieczna? Nie sed 'N; /^\(.*\)\n\1$/!P; D'robi tego samego? Nie mogę wymyślić przykładu, w którym oba są różne na moim komputerze (fwiw próbowałem na końcu pustego wiersza w obu wersjach i obie były w porządku).
eddi
1
Prawie 7 lat później nikt nie odpowiedział na @amichair ... <sniff> mnie zasmuca. ;) W każdym razie [ -~]reprezentuje zakres znaków ASCII od 0x20 (spacja) do 0x7E (tylda). Są one uważane za drukowalne znaki ASCII (strona połączona ma również 0x7F / delete, ale to nie wydaje się właściwe). To sprawia, że rozwiązanie jest zepsute dla każdego, kto nie używa ASCII lub kto używa, powiedzmy, znaków tabulacji. Bardziej przenośny [^\n]zawiera o wiele więcej znaków ... wszystkie z wyjątkiem jednego, w rzeczywistości.
Warstwa B,
15
Jednowierszowy Perl podobny do rozwiązania awk @ jonas:
perl -ne 'print if ! $x{$_}++' file
Ta odmiana usuwa końcowe spacje przed porównaniem:
perl -lne 's/\s*$//; print if ! $x{$_}++' file
Ta odmiana edytuje plik lokalnie:
perl -i -ne 'print if ! $x{$_}++' file
Ta odmiana edytuje plik w miejscu i tworzy kopię zapasową file.bak
Jedna linijka, którą Andre Miller opublikował powyżej, działa z wyjątkiem ostatnich wersji seda, w których plik wejściowy kończy się pustą linią i brakiem znaków. Na moim Macu mój procesor po prostu się obraca.
Nieskończona pętla, jeśli ostatnia linia jest pusta i nie ma żadnych znaków :
sed '$!N; /^\(.*\)\n\1$/!P; D'
Nie zawiesza się, ale tracisz ostatnią linię
sed '$d;N; /^\(.*\)\n\1$/!P; D'
Wyjaśnienie znajduje się na samym końcu FAQ seda :
Opiekun GNU sed uważał, że pomimo problemów z przenośnością, jakie
by to spowodowało, zmiana polecenia N, aby wydrukować (zamiast
usuwać) przestrzeń wzorców była bardziej zgodna z intuicją
dotyczącą tego, jak powinno się zachować polecenie „dołączenia następnej linii” .
Kolejnym faktem przemawiającym za zmianą było to, że "{N; polecenie;}"
usunie ostatnią linię, jeśli plik ma nieparzystą liczbę linii, ale
wypisze ostatnią, jeśli plik ma parzystą liczbę linii.
Aby przekonwertować skrypty, które używały poprzedniego zachowania N (usuwając
przestrzeń wzorca po osiągnięciu EOF) na skrypty kompatybilne ze
wszystkimi wersjami seda, zmień samotne „N”; na „$ d; N;” .
print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.
Wyjaśnia:
$!N;: jeśli bieżąca linia NIE jest ostatnią linią, użyj Npolecenia, aby wczytać następną linię pattern space.
/^(.*)\n\1$/!P: jeśli zawartość bieżącej pattern spacejest duplicate stringoddzielona dwoma \n, co oznacza, że następna linia jest samelinią bieżącą, NIE możemy jej wydrukować zgodnie z naszą podstawową ideą; inaczej, co oznacza, że bieżący wiersz to ostatni wygląd wszystkich jego duplikatów kolejnych linii, możemy teraz użyć Ppolecenia drukowania znaków w bieżącym pattern spaceutil \n( \nrównież drukowane).
D: Używamy Dpolecenia, aby usunąć znaki w bieżącym pattern spaceutil \n( \ntakże usuniętych), a następnie treść pattern spacejest następna linia.
a Dpolecenie wymusi sedprzejście do swojego FIRSTpolecenia $!N, ale NIE odczyta następnego wiersza z pliku lub standardowego strumienia wejściowego.
Drugie rozwiązanie jest łatwe do zrozumienia (ode mnie):
print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.
Wyjaśnia:
odczytaj nowy wiersz ze strumienia wejściowego lub pliku i wydrukuj go raz.
użyj :looppolecenia ustaw labelnazwany loop.
użyj, Naby przeczytać następny wiersz w pliku pattern space.
użyj s/^(.*)\n\1$/\1/do usunięcia bieżącej linii, jeśli następna linia jest taka sama jak bieżąca linia, używamy spolecenia, aby wykonać deleteakcję.
jeśli spolecenie zostanie wykonane pomyślnie, użyj tlooppolecenia force, sedaby przeskoczyć do labelnazwanego loop, co spowoduje wykonanie tej samej pętli do następnych wierszy, przy czym nie ma zduplikowanych kolejnych wierszy linii, która jest latest printed; w przeciwnym razie użyj Dpolecenia do deletelinii, która jest taka sama jak latest-printed linei wymuś sedprzejście do pierwszego polecenia, które jest ppoleceniem, zawartość bieżącej pattern spacejest następną nową linią.
uniq dałby się zwieść końcowym spacjom i tabulatorom. Aby naśladować sposób, w jaki człowiek dokonuje porównania, przed porównaniem przycinam wszystkie końcowe spacje i tabulatory.
Myślę, że $! N; potrzebuje nawiasów klamrowych, bo inaczej to trwa, i to jest przyczyną nieskończonej pętli.
Mam bash 5.0 i sed 4.7 w Ubuntu 20.10. Druga linijka nie działała przy dopasowaniu zestawu znaków.
Trzy warianty, pierwsza eliminująca sąsiednie powtarzające się wiersze, druga eliminująca powtarzające się wiersze wszędzie tam, gdzie występują, trzecia eliminująca wszystkie z wyjątkiem ostatniego wystąpienia wierszy w pliku.
# First line in a set of duplicate lines is kept, rest are deleted.# Emulate human eyes on trailing spaces and tabs by trimming those.# Use after norepeat() to dedupe blank lines.dedupe() {
sed -E '
$!{
N;
s/[ \t]+$//;
/^(.*)\n\1$/!P;
D;
}
';
}
# Delete duplicate, nonconsecutive lines from a file. Ignore blank# lines. Trailing spaces and tabs are trimmed to humanize comparisons# squeeze blank lines to onenorepeat() {
sed -n -E '
s/[ \t]+$//;
G;
/^(\n){2,}/d;
/^([^\n]+).*\n\1(\n|$)/d;
h;
P;
';
}
lastrepeat() {
sed -n -E '
s/[ \t]+$//;
/^$/{
H;
d;
};
G;
# delete previous repeated line if found
s/^([^\n]+)(.*)(\n\1(\n.*|$))/\1\2\4/;
# after searching for previous repeat, move tested last line to end
s/^([^\n]+)(\n)(.*)/\3\2\1/;
$!{
h;
d;
};
# squeeze blank lines to one
s/(\n){3,}/\n\n/g;
s/^\n//;
p;
';
}
Co to jest plik tekstowy o rozmiarze 20 GB? Za wolno.
Alexander Lubyagin
Jak zawsze, jest bezużyteczny. W każdym razie robi to już samodzielnie i nie wymaga, aby dane wejściowe zawierały dokładnie jedno słowo w wierszu. catuniq
uniq
wystarczy sam.awk
większych plików, ale będzie to wymagało sporo zasobów.Odpowiedzi:
awk '!seen[$0]++' file.txt
seen
jest tablicą asocjacyjną, do której Awk przekaże każdą linię pliku. Jeśli linia nie znajduje się w tablicy,seen[$0]
zostanie obliczona jako fałsz.!
Jest operator logiczny NOT i odwrócić false na true. Awk wypisze wiersze, w których wyrażenie przyjmuje wartość true. Te++
przyrostyseen
tak, żeseen[$0] == 1
po raz pierwszy wiersz zostanie znaleziony, a następnieseen[$0] == 2
, i tak dalej.Awk ocenia wszystko oprócz
0
i""
(pusty ciąg) na wartość true. Jeśli zostanie wstawiony zduplikowany wiersz,seen
wówczas!seen[$0]
zostanie obliczony jako fałsz i wiersz nie zostanie zapisany na wyjściu.źródło
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Z http://sed.sourceforge.net/sed1line.txt : (Nie pytaj mnie, jak to działa ;-))
# 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'
źródło
$!
część jest konieczna? Niesed 'N; /^\(.*\)\n\1$/!P; D'
robi tego samego? Nie mogę wymyślić przykładu, w którym oba są różne na moim komputerze (fwiw próbowałem na końcu pustego wiersza w obu wersjach i obie były w porządku).[ -~]
reprezentuje zakres znaków ASCII od 0x20 (spacja) do 0x7E (tylda). Są one uważane za drukowalne znaki ASCII (strona połączona ma również 0x7F / delete, ale to nie wydaje się właściwe). To sprawia, że rozwiązanie jest zepsute dla każdego, kto nie używa ASCII lub kto używa, powiedzmy, znaków tabulacji. Bardziej przenośny[^\n]
zawiera o wiele więcej znaków ... wszystkie z wyjątkiem jednego, w rzeczywistości.Jednowierszowy Perl podobny do rozwiązania awk @ jonas:
perl -ne 'print if ! $x{$_}++' file
Ta odmiana usuwa końcowe spacje przed porównaniem:
perl -lne 's/\s*$//; print if ! $x{$_}++' file
Ta odmiana edytuje plik lokalnie:
perl -i -ne 'print if ! $x{$_}++' file
Ta odmiana edytuje plik w miejscu i tworzy kopię zapasową
file.bak
perl -i.bak -ne 'print if ! $x{$_}++' file
źródło
Alternatywny sposób używania Vima (kompatybilny z Vi) :
Usuń zduplikowane, kolejne wiersze z pliku:
vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq
Usuń zduplikowane, niepuste i niepuste wiersze z pliku:
vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq
źródło
Jedna linijka, którą Andre Miller opublikował powyżej, działa z wyjątkiem ostatnich wersji seda, w których plik wejściowy kończy się pustą linią i brakiem znaków. Na moim Macu mój procesor po prostu się obraca.
Nieskończona pętla, jeśli ostatnia linia jest pusta i nie ma żadnych znaków :
sed '$!N; /^\(.*\)\n\1$/!P; D'
Nie zawiesza się, ale tracisz ostatnią linię
sed '$d;N; /^\(.*\)\n\1$/!P; D'
Wyjaśnienie znajduje się na samym końcu FAQ seda :
źródło
Pierwsze rozwiązanie również pochodzi z http://sed.sourceforge.net/sed1line.txt
$ 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
podstawową ideą jest:
print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.
Wyjaśnia:
$!N;
: jeśli bieżąca linia NIE jest ostatnią linią, użyjN
polecenia, aby wczytać następną liniępattern space
./^(.*)\n\1$/!P
: jeśli zawartość bieżącejpattern space
jestduplicate string
oddzielona dwoma\n
, co oznacza, że następna linia jestsame
linią bieżącą, NIE możemy jej wydrukować zgodnie z naszą podstawową ideą; inaczej, co oznacza, że bieżący wiersz to ostatni wygląd wszystkich jego duplikatów kolejnych linii, możemy teraz użyćP
polecenia drukowania znaków w bieżącympattern space
util\n
(\n
również drukowane).D
: UżywamyD
polecenia, aby usunąć znaki w bieżącympattern space
util\n
(\n
także usuniętych), a następnie treśćpattern space
jest następna linia.D
polecenie wymusised
przejście do swojegoFIRST
polecenia$!N
, ale NIE odczyta następnego wiersza z pliku lub standardowego strumienia wejściowego.Drugie rozwiązanie jest łatwe do zrozumienia (ode mnie):
$ 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
podstawową ideą jest:
print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.
Wyjaśnia:
:loop
polecenia ustawlabel
nazwanyloop
.N
aby przeczytać następny wiersz w plikupattern space
.s/^(.*)\n\1$/\1/
do usunięcia bieżącej linii, jeśli następna linia jest taka sama jak bieżąca linia, używamys
polecenia, aby wykonaćdelete
akcję.s
polecenie zostanie wykonane pomyślnie, użyjtloop
polecenia force,sed
aby przeskoczyć dolabel
nazwanegoloop
, co spowoduje wykonanie tej samej pętli do następnych wierszy, przy czym nie ma zduplikowanych kolejnych wierszy linii, która jestlatest printed
; w przeciwnym razie użyjD
polecenia dodelete
linii, która jest taka sama jaklatest-printed line
i wymuśsed
przejście do pierwszego polecenia, które jestp
poleceniem, zawartość bieżącejpattern space
jest następną nową linią.źródło
busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
Można to osiągnąć za pomocą awk
Below Line wyświetli unikalne wartości
Te unikalne wartości można umieścić w nowym pliku
nowy plik uniq_file_name będzie zawierał tylko unikalne wartości, bez duplikatów
źródło
uniq dałby się zwieść końcowym spacjom i tabulatorom. Aby naśladować sposób, w jaki człowiek dokonuje porównania, przed porównaniem przycinam wszystkie końcowe spacje i tabulatory.
Myślę, że $! N; potrzebuje nawiasów klamrowych, bo inaczej to trwa, i to jest przyczyną nieskończonej pętli.
Mam bash 5.0 i sed 4.7 w Ubuntu 20.10. Druga linijka nie działała przy dopasowaniu zestawu znaków.
Trzy warianty, pierwsza eliminująca sąsiednie powtarzające się wiersze, druga eliminująca powtarzające się wiersze wszędzie tam, gdzie występują, trzecia eliminująca wszystkie z wyjątkiem ostatniego wystąpienia wierszy w pliku.
pastebin
# First line in a set of duplicate lines is kept, rest are deleted. # Emulate human eyes on trailing spaces and tabs by trimming those. # Use after norepeat() to dedupe blank lines. dedupe() { sed -E ' $!{ N; s/[ \t]+$//; /^(.*)\n\1$/!P; D; } '; } # Delete duplicate, nonconsecutive lines from a file. Ignore blank # lines. Trailing spaces and tabs are trimmed to humanize comparisons # squeeze blank lines to one norepeat() { sed -n -E ' s/[ \t]+$//; G; /^(\n){2,}/d; /^([^\n]+).*\n\1(\n|$)/d; h; P; '; } lastrepeat() { sed -n -E ' s/[ \t]+$//; /^$/{ H; d; }; G; # delete previous repeated line if found s/^([^\n]+)(.*)(\n\1(\n.*|$))/\1\2\4/; # after searching for previous repeat, move tested last line to end s/^([^\n]+)(\n)(.*)/\3\2\1/; $!{ h; d; }; # squeeze blank lines to one s/(\n){3,}/\n\n/g; s/^\n//; p; '; }
źródło
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
Usuwa zduplikowane wiersze za pomocą awk.
źródło
cat
uniq