2012-10-20 2 views
0

J'essaye d'écrire un script bash pour utiliser sed pour supprimer certaines lignes d'un fichier. Les numéros de ligne sont stockés dans un autre fichier dans l'ordre inverse. La commande que je suis en train de faire est la suivante:Le script Bash utilisant sed pour supprimer une ligne spécifique ne fonctionne pas

sed -e '{lineNumber}d' ./file.txt 

C'est ce que j'ai jusqu'à présent, mais il ne fonctionne pas

while read -r line 
do 
    sed -e "/${line}d" ./file.txt 
done < ./lineNum.txt 

Je reçois l'erreur suivante:
sed: -e expression # 1, char 4: adresse unterminated regex

+0

donc vous avez un deuxième fichier qui répertorie les lignes qui doivent être supprimées du premier fichier? –

+0

Merci. Le script fonctionne mais il ne supprime pas les lignes. Est-ce que j'utilise la commande sed mal? – MFK

+0

@ sampson-chen oui – MFK

Répondre

1
while read -r line; do sed -i "${line} d" ./file.txt; done < ./linenum.txt 

Cela fonctionne (je pense que votre problème est d'utiliser -e); mais ce n'est pas efficace. Il peut être préférable de passer plusieurs lignes à la fois pour sed, pour éviter de lire et d'écrire le fichier une fois par ligne. Par exemple, vous pourriez transformer linenum.txt en quelque chose comme "6 d; 2 d; 1 d;" puis passez-le à sed pour un traitement scoop.

+0

Ça marche! Merci beaucoup. Et merci pour la suggestion de le rendre plus efficace, je vais transformer le fichier dans ce format! – MFK

1

Vous pouvez effectuer les modifications directement à l'aide sed sans utiliser une boucle:

sed 's/.*/&d/' lineNum.txt | sed -i -f - file.txt 
+1

belle oneliner. Sur cette base, je voudrais aussi suggérer 'sed -n '/ ^[0-9][0-9]*$/{s/.*/ &d/; p}' lineNum.txt | sed -i -f - file.txt' pour éviter les problèmes avec les lignes mal formatées dans lineNum (en particulier les lignes vides qui génèreraient un 'd' déshabillé et supprimeraient tout le contenu du fichier) –

+0

@GermanGarcia: Nice catch! Mais je m'attendrais 'sed -n '/^[0-9] [0-9] * $/{s/$/d /; p}' lineNum.txt | sed -i -f - file.txt' pour être plus efficace et (deviner) 'sed -n '/^[0-9] [0-9] * $/s/$/d/p' lineNum.txt | sed -i -f - file.txt' pour faire de même avec moins de code. Aussi juste par curiosité: Que se passerait-il si un nombre commence par des zéros en tête ou si une ligne contient juste zéro? Est-ce que '[1-9] [0-9] *' serait plus robuste? – mschilli

3

En fait, ce que vous avez fait le mal est ce

sed -e "/${line}d" ./file.txt 

Vous voyez, sed a cette syntaxe

sed -e "/REGEX/d" ./file.txt 

qui supprime toutes les lignes contenant des correspondances à 0 ModèlePuisque vous avez le premier /, sed pense que vous essayez d'utiliser la correspondance regex, donc il est dit unterminated address regex.

Le correctif minimal requis est tout simplement enlever la barre oblique inverse incriminée, à savoir

sed -e "${line}d" ./file.txt 

Mis à: Pas une solution sed comme OP a demandé, mais ce ne OP veut plus efficacement.

awk 'NR==FNR {arr[$0]++; next} {if (!arr[FNR]) print }' linenum.txt file.txt 
+1

Au lieu de '{if (! Arr [FNR]) print}', vous pouvez écrire: '! (FNR in arr)'. C'est un peu plus propre. HTH. – Steve

+1

Bon appel. Je peux même simplement utiliser '! Arr [FNR]', mais je pense que '! (FNR in arr)' est meilleur car il ne crée pas l'élément s'il n'existait pas auparavant. – doubleDown

2

Tant qu'il n'y a pas outrageusement de lignes à supprimer et vous ne travaillez pas sur un système avec une version limitée de sed lamentablement (à un moment donné, sed sur HP-UX a été limitée à environ 100 commandes), vous pouvez utiliser:

sed 's/$/d/' linenum.txt | sed -f - file.txt 

Il utilise le premier sed pour convertir les numéros de ligne dans les commandes de suppression (notez qu'une partie de votre problème était une barre oblique indésirable parasite) et dit alors à lire la deuxième sed son script à partir de l'entrée standard (-f -) et l'appliquer à file.txt.

Ce qui précède fonctionne avec GNU sed; il n'a pas fonctionné avec BSD sed sur Mac OS X 10.7.5 (sed: -: No such file or directory). Testez-le avant de l'utiliser sur votre système.

Bien sûr, si vous avez une version suffisamment récente de bash (fonctionne avec bash 4.2 mais pas avec 3.2), vous pouvez utiliser la « substitution de processus » pour contourner la limitation de sed:

sed -f <(sed 's/$/d/' linenum.txt) file.txt 

Si cela ne fonctionne pas non plus, vous pouvez écrire la sortie de la première commande sed à un fichier, puis utiliser ce fichier (temporaire) comme nom pour le script sed. Donc, il y a beaucoup de façons de le faire. Cependant, n'importe quoi sur 3 processus (deux exécutions de sed et un de rm) est extravagant. Ce n'est probablement pas un problème si vous n'avez besoin de le faire qu'une seule fois, mais cela peut poser problème si vous devez le faire plusieurs fois par minute.

+0

Je devine juste mais je pense que le problème 'sed: -: No tel fichier ou répertoire' pourrait aussi être résolu en utilisant'/dev/stdin' à la place. Impossible d'essayer puisque je ne suis pas sur un Mac. – mschilli

Questions connexes