Jak usunąć z pliku tekstowego wszystkie wiersze zawierające określony ciąg?

1789

Jak miałbym użyć sed, aby usunąć wszystkie wiersze w pliku tekstowym zawierającym określony ciąg?

Mechaniczna pomarańcza
źródło

Odpowiedzi:

2758

Aby usunąć linię i wydrukować wyjście na standardowe wyjście:

sed '/pattern to match/d' ./infile

Aby bezpośrednio zmodyfikować plik - nie działa z sedem BSD:

sed -i '/pattern to match/d' ./infile

To samo, ale dla BSD sed (Mac OS X i FreeBSD) - nie działa z GNU sed:

sed -i '' '/pattern to match/d' ./infile

Aby bezpośrednio zmodyfikować plik (i utworzyć kopię zapasową) - działa z BSD i GNU sed:

sed -i.bak '/pattern to match/d' ./infile
SiegeX
źródło
13
Dzięki, ale wydaje się, że nie usuwa go z pliku, ale po prostu drukuje zawartość pliku tekstowego bez tego ciągu.
Mechaniczna pomarańcza,
115
@ A Mechaniczna: tak, musisz przekierować dane wyjściowe do nowego pliku z czymś podobnym sed '/pattern to match/d' ./infile > ./newfilelub jeśli chcesz dokonać edycji w miejscu, możesz dodać -iflagę do sed jak w sed -i '/pattern to match/d' ./infile. Zauważ, że -iflaga wymaga GNU sed i nie jest przenośna
SiegeX
16
Dla niektórych smaków sed; Flaga „-i” sed wymagała rozszerzenia. (np. sed -i.backup '/pattern to match/d' ./infile) Dzięki temu wprowadziłem zmiany w miejscu.
avelis
9
@SiegeX Jeszcze lepiej, nie stosuj poleceń takich jak seddo plików, które nie są kontrolowane przez wersję.
MatrixFrog
84
Jeszcze jedna uwaga dla użytkowników Mac OS X: z jakiegoś powodu flaga -i wymaga przekazania argumentu, nawet jeśli jest to tylko pusty ciąg znaków, na przykład sed -i '' '/pattern/d' ./infile.
geerlingguy
631

Istnieje wiele innych sposobów usuwania linii z określonym ciągiem znaków sed:

AWK

awk '!/pattern/' file > temp && mv temp file

Rubin (1.9+)

ruby -i.bak -ne 'print if not /test/' file

Perl

perl -ni.bak -e "print unless /pattern/" file

Shell (bash 3.2 i nowsze)

while read -r line
do
  [[ ! $line =~ pattern ]] && echo "$line"
done <file > o
mv o file

GNU grep

grep -v "pattern" file > temp && mv temp file

I oczywiście sed(wydruk odwrotny jest szybszy niż faktyczne usunięcie):

sed -n '/pattern/!p' file
kurumi
źródło
4
jak usunąć określoną linię ze wzorem, a także linię bezpośrednio nad nią? Mam mandat z tysiącami takich linii pomiędzy różnymi danymi.
oortcloud_domicile
1
W OS / X odmiana powłoki nie zachowuje wiodących spacji, ale odmiana grep -v działała dla mnie dobrze.
Paul Beusterien
13
sedprzykład mieć inny problem, to tylko greps! powinno być coś takiego sed -n -i '/pattern/!p' file.
caesarsol
8
Wersja grep nie działa, gdy każda linia pasuje do wzorca. Lepiej zrobić: grep -v "pattern" file > temp; mv temp filemoże to dotyczyć niektórych innych przykładów w zależności od wartości zwracanej.
Chris Maes,
1
„drukowanie odwrotności jest szybsze niż faktyczne usunięcie” - Nie na moim komputerze (MacBook Air 2012, OS X 10.13.2). Tworzenie pliku: seq -f %f 10000000 >foo.txt. sed d: time sed -i '' '/6543210/d' foo.txtprawdziwe 0m9.294s. sed! p: time sed -i '' -n '/6543210/!p' foo.txtreal 0m13.671s. (W przypadku mniejszych plików różnica jest większa.)
jcsahnwaldt mówi GoFundMonica
252

Możesz użyć sed, aby zamienić linie w pliku. Wydaje się jednak, że jest znacznie wolniejszy niż użycie grep dla odwrotności do drugiego pliku, a następnie przeniesienie drugiego pliku na oryginał.

na przykład

sed -i '/pattern/d' filename      

lub

grep -v "pattern" filename > filename2; mv filename2 filename

Pierwsze polecenie i tak trwa 3 razy dłużej na moim komputerze.

slashdottir
źródło
19
Głosuj też na swoją odpowiedź, tylko dlatego, że próbowałeś porównania wydajności!
anuragw
4
+1 za oferowanie opcji zastąpienia bieżącego pliku linią grep.
Rhyuk,
2
Drugie rozwiązanie „grep” jest również lepsze w przypadku dużych plików
przypomina
3
Jestem ciekawy, jaka byłaby różnica w wydajności, gdyby tak byłosed '/pattern/d' filename > filename2; mv filename2 filename
Pete
8
(używając Ubuntu / usr / share / dict / words) grep i mv: 0.010s | sed w miejscu: 0,197s | sed i mv: 0,031s
ReactiveRaven
77

Łatwy sposób na zrobienie tego z GNU sed:

sed --in-place '/some string here/d' yourfile
Kevin Nguyen
źródło
55
Przydatna wskazówka dla innych, którzy natkną się na ten wątek pytań i nowości w skryptach powłoki: krótkie opcje są odpowiednie do jednorazowego użycia w wierszu poleceń, ale dłuższe opcje powinny być preferowane w skryptach, ponieważ są bardziej czytelne.
Dennis,
3
+1 za flagę --in-place. Muszę to przetestować na plikach chronionych uprawnieniami. (trzeba wykonać szorowanie użytkownika).
Bee Kay
8
Zauważ, że długa opcja jest dostępna tylko w GNU sed. Użytkownicy komputerów Mac i BSD będą musieli zainstalować gsed, aby zrobić to w ten sposób.
Matt
Kolejna wskazówka: jeśli wyrażenie regularne nie pasuje, wypróbuj -ropcję (lub -E, w zależności od wersji). Umożliwia to korzystanie z regex metaznakami +, ?, {...}i (...).
rjh
To poprawna odpowiedź, gdy na dysku nie ma już miejsca i nie można skopiować tekstu do innego pliku. To polecenie robi to, co zostało zakwestionowane?
ferreirabraga
38

Możesz rozważyć użycie ex(który jest standardowym edytorem uniksowym opartym na poleceniach):

ex +g/match/d -cwq file

gdzie:

  • +wykonuje dane polecenie Ex ( man ex), tak samo jak to, -cktóre wykonuje wq(zapisz i wyjdź)
  • g/match/d- Polecenie Ex do usuwania linii z danym match, patrz: Moc g

Powyższy przykład jest zgodną z POSIX metodą edycji na miejscu pliku zgodnie z tym postem w specyfikacji Unix.SE i POSIX dlaex .


Różnica sedpolega na tym, że:

sedto iterator S tream ED , a nie edytor plików. BashFAQ

Chyba że podoba Ci się nieobsługiwany kod, narzut I / O i inne złe skutki uboczne. Zasadniczo niektóre parametry (takie jak na miejscu / -i) są niestandardowymi rozszerzeniami FreeBSD i mogą nie być dostępne w innych systemach operacyjnych.

kenorb
źródło
5
to świetnie ... kiedy robię man exdaje mi mężczyznę vim, wydaje się, exjest częścią vim ... gdybym rozumieć prawo to oznacza dla składni wzór matchjest vimregex.com która jest podobna, ale różni się od POSIX PCRE i smaków?
Anentropic
1
:g to polecenie zgodne z POSIX, z pewnymi niewielkimi różnicami . Zakładam, że PCRE było na nim oparte.
kenorb
16

Walczyłem z tym na Macu. Dodatkowo musiałem to zrobić, używając zmiennej zamiany.

Więc użyłem:

sed -i '' "/$pattern/d" $file

gdzie $filejest plik, w którym konieczne jest usunięcie, i $patternwzór, który należy dopasować do usunięcia.

Wybrałem ''z tego komentarza .

Należy zwrócić uwagę na użycie podwójnych cudzysłowów w "/$pattern/d". Zmienna nie będzie działać, gdy użyjemy pojedynczych cudzysłowów.

Aniket Sinha
źródło
3
Mac sedwymaga parametru po -i, więc jeśli nie chcesz kopii zapasowej, nadal musisz dodać pusty ciąg:-i ''
wisbucky,
Do użytku z powłoką sed -i "/$pattern/d" $file. Dziękuję za Twoją odpowiedź.
ashwaqar
14

Zrobiłem mały test porównawczy z plikiem, który zawiera około 345 000 linii. Sposób z grepwydaje się być około 15 razy szybszy niż sedmetoda w tym przypadku.

Próbowałem zarówno z, jak i bez ustawienia LC_ALL = C, wydaje się, że nie zmienia to znacząco czasów. Wyszukiwany ciąg (CDGA_00004.pdbqt.gz.tar) znajduje się gdzieś pośrodku pliku.

Oto polecenia i czasy:

time sed -i "/CDGA_00004.pdbqt.gz.tar/d" /tmp/input.txt

real    0m0.711s
user    0m0.179s
sys     0m0.530s

time perl -ni -e 'print unless /CDGA_00004.pdbqt.gz.tar/' /tmp/input.txt

real    0m0.105s
user    0m0.088s
sys     0m0.016s

time (grep -v CDGA_00004.pdbqt.gz.tar /tmp/input.txt > /tmp/input.tmp; mv /tmp/input.tmp /tmp/input.txt )

real    0m0.046s
user    0m0.014s
sys     0m0.019s
Jadzia
źródło
Na jakiej platformie jesteś? Z jakich wersji sed / perl / grep korzystasz?
hagello
Platformą, z której korzystam, jest Linux (Gentoo). Wersja sed to GNU sed v 4.2.2, wersja perl perl 5 (Nie mogę powiedzieć, z której wersji korzystałem podczas testu), a grep (GNU) to wersja 3.0.
Jadzia
14

Możesz także użyć tego:

 grep -v 'pattern' filename

Tutaj -vwydrukuje się tylko inny wzór niż twój (co oznacza dopasowanie odwrócone).

Bhuvanesh
źródło
Jak mogę usunąć wiersze z katalogu zawierającego określony ciąg
namannimmo
13

Aby uzyskać podobny wynik, grepmożesz to zrobić:

echo "$(grep -v "pattern" filename)" >filename
Jahid
źródło
4
Jest to dobre tylko dla bashpowłoki lub podobnego (nie tcsh).
esmit
4
perl -i    -nle'/regexp/||print' file1 file2 file3
perl -i.bk -nle'/regexp/||print' file1 file2 file3

Pierwsze polecenie edytuje plik (i) w miejscu (-i).

Drugie polecenie robi to samo, ale zachowuje kopię lub kopię zapasową oryginalnych plików, dodając .bk do nazw plików (.bk można zmienić na dowolne).

Kjetil S.
źródło
2

echo -e "/thing_to_delete\ndd\033:x\n" | vim file_to_edit.txt

Shizzmo
źródło
2

Na wypadek, gdyby ktoś chciał to zrobić dla dokładnego dopasowania ciągów, możesz użyć -wflagi w grep - w dla całości. To znaczy, na przykład, jeśli chcesz usunąć linie o numerze 11, ale zachowaj linie o numerze 111:

-bash-4.1$ head file
1
11
111

-bash-4.1$ grep -v "11" file
1

-bash-4.1$ grep -w -v "11" file
1
111

Działa również z -fflagą, jeśli chcesz wykluczyć kilka dokładnych wzorów jednocześnie. Jeśli „czarna lista” to plik z kilkoma wzorami w każdej linii, który chcesz usunąć z „pliku”:

grep -w -v -f blacklist file
FatihSarigol
źródło
Trochę mylące. -w, --word-regexp Select only those lines containing matches that form whole words.vs.-x, --line-regexp Select only those matches that exactly match the whole line. For a regular expression pattern, this is like parenthesizing the pattern and then surrounding it with ^ and $.
Sai
1
cat filename | grep -v "pattern" > filename.1
mv filename.1 filename
Andrey Izman
źródło
Nadpisujesz plik, gdy jest on nadal używany.
Davor Cubranic
@DavorCubranic naprawiono
Andrey Izman
0

aby wyświetlić traktowany tekst w konsoli

cat filename | sed '/text to remove/d' 

aby zapisać traktowany tekst w pliku

cat filename | sed '/text to remove/d' > newfile

aby dołączyć przetworzone informacje tekstowe do istniejącego pliku

cat filename | sed '/text to remove/d' >> newfile

aby leczyć już przetworzony tekst, w tym przypadku usuń więcej wierszy tego, co zostało usunięte

cat filename | sed '/text to remove/d' | sed '/remove this too/d' | more

| morepokaże tekst kawałkami jednej strony na raz.

nassim
źródło
0

Można używać starych dobrych ededytować plik w podobny sposób do odpowiedzi , które wykorzystuje ex. Dużą różnicą w tym przypadku jest to, że edprzyjmuje polecenia za pomocą standardowego wejścia, a nie argumentów wiersza poleceń, jak exmożna. Kiedy używasz go w skrypcie, zwykłym sposobem na przystosowanie się do tego jest użycie printfpoleceń do niego:

printf "%s\n" "g/pattern/d" w | ed -s filename

lub z heredoc:

ed -s filename <<EOF
g/pattern/d
w
EOF
Shawn
źródło