Czy istnieje sposób na usunięcie zduplikowanych wierszy w pliku w systemie Unix?
Mogę to zrobić za pomocą poleceń sort -ui uniq, ale chcę użyć sedlub awk. Czy to jest możliwe?
Czy istnieje sposób na usunięcie zduplikowanych wierszy w pliku w systemie Unix?
Mogę to zrobić za pomocą poleceń sort -ui uniq, ale chcę użyć sedlub awk. Czy to jest możliwe?
awkwiększych plików, ale będzie to wymagało sporo zasobów.
Odpowiedzi:
awk '!seen[$0]++' file.txt
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.
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'
$!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).
[ -~]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
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;” .
$ 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ż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.Dpolecenie wymusi sedprzejście do swojego FIRSTpolecenia $!N, ale NIE odczyta następnego wiersza z pliku lub standardowego strumienia wejściowego.$ 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:
:looppolecenia ustaw labelnazwany loop.Naby przeczytać następny wiersz w pliku pattern space.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ę.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ą.busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
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 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;
';
}
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
Usuwa zduplikowane wiersze za pomocą awk.
catuniq
uniqwystarczy sam.