Comment supprimer d'un fichier texte toutes les lignes contenant une chaîne spécifique?

1790

Comment utiliser sed pour supprimer toutes les lignes d'un fichier texte contenant une chaîne spécifique?

Une orange mécanique
la source

Réponses:

2760

Pour supprimer la ligne et imprimer la sortie en sortie standard:

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

Pour modifier directement le fichier - ne fonctionne pas avec BSD sed:

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

Idem, mais pour BSD sed (Mac OS X et FreeBSD) - ne fonctionne pas avec GNU sed:

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

Pour modifier directement le fichier (et créer une sauvegarde) - fonctionne avec BSD et GNU sed:

sed -i.bak '/pattern to match/d' ./infile
SiegeX
la source
13
Merci, mais il ne semble pas l'effacer du fichier mais il suffit d'imprimer le contenu du fichier texte sans cette chaîne.
Un Clockwork Orange
115
@A Clockwork: oui, vous devez rediriger la sortie vers un nouveau fichier avec quelque chose comme sed '/pattern to match/d' ./infile > ./newfileou si vous voulez faire une modification sur place, vous pouvez ajouter le -idrapeau à sed comme dans sed -i '/pattern to match/d' ./infile. Notez que le -idrapeau nécessite GNU sed et n'est pas portable
SiegeX
16
Pour certaines saveurs de sed; L'indicateur "-i" de sed nécessitait une extension. (par exemple sed -i.backup '/pattern to match/d' ./infile) Cela m'a permis de faire des modifications sur place.
avelis
9
@SiegeX Mieux encore, n'appliquez pas de commandes comme sedà tous les fichiers dont la version n'est pas contrôlée.
MatrixFrog
84
Une dernière remarque pour les utilisateurs de Mac OS X: pour une raison quelconque, l'indicateur -i nécessite la transmission d'un argument, même s'il ne s'agit que d'une chaîne vide, comme sed -i '' '/pattern/d' ./infile.
geerlingguy
631

Il existe de nombreuses autres façons de supprimer des lignes avec une chaîne spécifique en plus sed:

AWK

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

Rubis (1.9+)

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

Perl

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

Shell (bash 3.2 et versions ultérieures)

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

Et bien sûr sed(l'impression de l'inverse est plus rapide que la suppression réelle):

sed -n '/pattern/!p' file
kurumi
la source
4
comment supprimer une ligne particulière avec un motif et aussi la ligne immédiatement au-dessus? J'ai une amende avec des milliers de telles lignes entre différentes données.
oortcloud_domicile
1
Sous OS / X, la variation du shell ne conserve pas les espaces de tête, mais la variation grep -v a bien fonctionné pour moi.
Paul Beusterien
13
l' sedexemple a un comportement différent, il ne fait que greps! ça devrait être quelque chose comme ça sed -n -i '/pattern/!p' file.
caesarsol
8
La version grep ne fonctionne pas lorsque chaque ligne correspond au modèle. Mieux vaut: grep -v "pattern" file > temp; mv temp filecela peut s'appliquer à certains des autres exemples en fonction de la valeur de retour.
Chris Maes
1
"l'impression de l'inverse est plus rapide que la suppression réelle" - Pas sur ma machine (2012 MacBook Air, OS X 10.13.2). Créer un fichier: seq -f %f 10000000 >foo.txt. sed d: time sed -i '' '/6543210/d' foo.txtréel 0m9.294s. sed! p: time sed -i '' -n '/6543210/!p' foo.txtréel 0m13.671s. (Pour les fichiers plus petits, la différence est plus grande.)
jcsahnwaldt dit GoFundMonica
252

Vous pouvez utiliser sed pour remplacer les lignes en place dans un fichier. Cependant, il semble être beaucoup plus lent que d'utiliser grep pour l'inverse dans un deuxième fichier, puis de déplacer le deuxième fichier sur l'original.

par exemple

sed -i '/pattern/d' filename      

ou

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

La première commande prend de toute façon 3 fois plus de temps sur ma machine.

slashdottir
la source
19
Votez aussi votre réponse, juste parce que vous avez essayé une comparaison des performances!
anuragw
4
+1 pour offrir l'option d'écraser le fichier actuel avec la ligne grep.
Rhyuk
2
La deuxième solution «grep» est également meilleure pour les fichiers volumineux
simoes
3
Je suis curieux de savoir quelle serait la différence de performances si elle l'étaitsed '/pattern/d' filename > filename2; mv filename2 filename
Pete
9
(en utilisant / usr / share / dict / words d'ubuntu) grep et mv: 0.010s | sed en place: 0.197s | sed et mv: 0,031s
ReactiveRaven
77

Le moyen facile de le faire, avec GNU sed:

sed --in-place '/some string here/d' yourfile
Kevin Nguyen
la source
56
Un conseil pratique pour ceux qui trébuchent sur ce fil de questions-réponses et qui sont nouveaux dans les scripts shell: les options courtes conviennent très bien pour des utilisations uniques sur la ligne de commande, mais les options longues devraient être préférées dans les scripts car elles sont plus lisibles.
Dennis
3
+1 pour le drapeau --en place. Je dois tester cela sur des fichiers protégés par des autorisations. (Je dois faire un peu de nettoyage par l'utilisateur.)
Bee Kay
8
Notez que l'option longue n'est disponible que sur GNU sed. Les utilisateurs de Mac et BSD devront installer gsed pour le faire de cette façon.
Matt
Autre astuce: si votre expression régulière ne semble pas correspondre, essayez l' -roption (ou -E, selon votre version). Cela permet l'utilisation de métacaractères regex +, ?, {...}et (...).
rjh
C'est la bonne réponse lorsque votre disque n'a plus d'espace et que vous ne pouvez pas copier le texte dans un autre fichier. Cette commande fait ce qui a été interrogé?
ferreirabraga
38

Vous pouvez envisager d'utiliser ex(qui est un éditeur standard basé sur les commandes Unix):

ex +g/match/d -cwq file

où:

  • +exécute la commande Ex donnée ( man ex), identique à celle -cqui s'exécute wq(écrire et quitter)
  • g/match/d- Commande Ex pour supprimer les lignes avec données match, voir: Puissance de g

L'exemple ci-dessus est une méthode compatible POSIX pour la modification sur place d'un fichier conformément à ce message dans les spécifications exUnix.SE et POSIX pour .


La différence avec sedest que:

sedest un éditeur S tream ED , pas un éditeur de fichiers. BashFAQ

Sauf si vous appréciez le code non transférable, les frais généraux d'E / S et certains autres effets secondaires néfastes. Donc, fondamentalement, certains paramètres (tels que in-place / -i) sont des extensions FreeBSD non standard et peuvent ne pas être disponibles sur d'autres systèmes d'exploitation.

kenorb
la source
5
c'est super ... quand je le fais, man excela me donne l'homme pour vim, il semble que cela exfait partie de vim ... si j'ai bien compris, cela signifie que la syntaxe du modèle matchest vimregex.com qui est similaire mais différent des saveurs POSIX et PCRE?
Anentropic
1
:g est une commande compatible POSIX avec quelques légères différences . Je suppose que PCRE était basé sur cela.
kenorb
16

J'avais du mal avec ça sur Mac. De plus, je devais le faire en utilisant le remplacement variable.

J'ai donc utilisé:

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

$fileest le fichier où la suppression est nécessaire et $patternest le modèle à associer pour la suppression.

J'ai choisi le ''de ce commentaire .

La chose à noter ici est l' utilisation de guillemets doubles dans "/$pattern/d". La variable ne fonctionnera pas lorsque nous utilisons des guillemets simples.

Aniket Sinha
la source
3
Mac sednécessite un paramètre après -i, donc si vous ne voulez pas de sauvegarde, vous devez toujours ajouter une chaîne vide:-i ''
wisbucky
Pour une utilisation shell sed -i "/$pattern/d" $file. Merci pour votre réponse.
ashwaqar
14

J'ai fait un petit benchmark avec un fichier qui contient environ 345 000 lignes. Le chemin avec grepsemble être environ 15 fois plus rapide que la sedméthode dans ce cas.

J'ai essayé à la fois avec et sans le réglage LC_ALL = C, il ne semble pas changer les temps de manière significative. La chaîne de recherche (CDGA_00004.pdbqt.gz.tar) se situe quelque part au milieu du fichier.

Voici les commandes et les horaires:

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
la source
Sur quelle plateforme êtes-vous? Quelles versions de sed / perl / grep utilisez-vous?
hagello
La plateforme que j'utilise est Linux (Gentoo). La version sed est GNU sed v 4.2.2, la version perl perl 5 (je ne peux pas dire quelle révision j'ai utilisée au moment du test) et grep (GNU) est la version 3.0.
Jadzia
14

Vous pouvez également utiliser ceci:

 grep -v 'pattern' filename

Ici -v, seuls les motifs seront imprimés (ce qui signifie une correspondance inversée).

Bhuvanesh
la source
Comment puis-je supprimer des lignes dans un répertoire qui contiennent une chaîne spécifique
namannimmo
13

Pour obtenir un résultat similaire sur place, grepvous pouvez procéder comme suit :

echo "$(grep -v "pattern" filename)" >filename
Jahid
la source
4
Ce n'est bon que pour le bashshell ou similaire (pas tcsh).
esmit
4
perl -i    -nle'/regexp/||print' file1 file2 file3
perl -i.bk -nle'/regexp/||print' file1 file2 file3

La première commande modifie le (s) fichier (s) en place (-i).

La deuxième commande fait la même chose mais conserve une copie ou une sauvegarde du ou des fichiers d'origine en ajoutant .bk aux noms de fichiers (.bk peut être changé en n'importe quoi).

Kjetil S.
la source
2

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

Shizzmo
la source
2

Juste au cas où quelqu'un voudrait le faire pour des correspondances exactes de chaînes, vous pouvez utiliser le -wdrapeau dans grep - w pour tout. C'est-à-dire, par exemple, si vous souhaitez supprimer les lignes qui ont le numéro 11, mais conservez les lignes avec le numéro 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

Il fonctionne également avec le -fdrapeau si vous souhaitez exclure plusieurs modèles exacts à la fois. Si "liste noire" est un fichier avec plusieurs modèles sur chaque ligne que vous souhaitez supprimer du "fichier":

grep -w -v -f blacklist file
FatihSarigol
la source
Un peu trompeur. -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
la source
Vous remplacez un fichier alors qu'il est encore en cours d'utilisation.
Davor Cubranic
@DavorCubranic fixed
Andrey Izman
0

pour afficher le texte traité dans la console

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

enregistrer le texte traité dans un fichier

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

pour ajouter des informations sur le texte traité à un fichier existant

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

pour traiter le texte déjà traité, dans ce cas, supprimez plus de lignes de ce qui a été supprimé

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

le | moretexte s'affichera en morceaux d'une page à la fois.

nassim
la source
0

Vous pouvez utiliser le bon vieux edpour modifier un fichier d'une manière similaire à la réponse qui utilise ex. La grande différence dans ce cas est qu'elle edprend ses commandes via une entrée standard, et non pas comme des arguments de ligne de commande comme excan. Lorsque vous l'utilisez dans un script, la manière habituelle de l'adapter est d'utiliser printfpour lui rediriger des commandes:

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

ou avec un hérédoc:

ed -s filename <<EOF
g/pattern/d
w
EOF
Shawn
la source