¿Eliminar números de línea específicos de un archivo de texto usando sed?

235

Quiero eliminar uno o más números de línea específicos de un archivo. ¿Cómo haría esto usando sed?

Justin Ethier
fuente
1
¿Puedes dar un ejemplo más específico de lo que quieres? ¿Cómo va a decidir qué líneas eliminar?
Mark Byers
Quizás vea también stackoverflow.com/questions/13272717/… y simplemente apliqueeit a la inversa (imprima si la clave no está en una matriz asociativa).
tripleee

Respuestas:

374

Si desea eliminar las líneas 5 a 10 y 12:

sed -e '5,10d;12d' file

Esto imprimirá los resultados en la pantalla. Si desea guardar los resultados en el mismo archivo:

sed -i.bak -e '5,10d;12d' file

Esto respaldará el archivo file.baky eliminará las líneas dadas.

Nota: Los números de línea comienzan en 1. La primera línea del archivo es 1, no 0.

Brian Campbell
fuente
32
No todos los unixes han gnu sed con "-i". No cometa el error de volver a "sed cmd file> file", lo que borrará su archivo.
pra
44
¿Qué pasa si quisiera eliminar la quinta línea hasta la última línea?
Jürgen Paul
14
@WearetheWorldsed -e '5,$d' file
Brian Campbell
1
@BrianCampbell ¿Qué debo hacer para eliminar solo una línea en particular?
Kanagavelu Sugumar
14
@KanagaveluSugumar sed -e '5d' file. La sintaxis es <address><command>; donde <address>puede ser una sola línea 5o un rango de líneas 5,10, y el comando delimina la línea o líneas dadas. Las direcciones también pueden ser expresiones regulares o el signo de dólar que $indica la última línea del archivo.
Brian Campbell
50

Puede eliminar una sola línea particular con su número de línea por

sed -i '33d' file

Esto eliminará la línea en el número de línea 33 y guardará el archivo actualizado.

amit
fuente
1
En mi caso "sed" eliminó una línea incorrecta. Así que uso este enfoque: sed -i '0,/<TARGET>/{/<NEW_VALUE>/d;}' '<SOME_FILE_NAME>'. ¡Gracias!
Eduardo Lucio
Lo mismo aquí, escribí un bucle y, extrañamente, algunos archivos perdieron la línea correcta, pero algunos archivos también perdieron otra línea, no tengo idea de qué salió mal. (GNU / Linux bash4.2) el siguiente comando awk funcionó bien en bucle
FatihSarigol
¡Tenga mucho cuidado de usar sort -r si está borrando de una lista de líneas, de lo contrario su primer sed cambiará los números de línea de todo lo demás! ...
Konchog
Para comentar sobre la eliminación de líneas incorrectas dentro de un bucle: asegúrese de comenzar con el número de línea más grande, de lo contrario, cada línea eliminada compensará la numeración de línea ...
Skippy le Grand Gourou
25

y awk también

awk 'NR!~/^(5|10|25)$/' file
ghostdog74
fuente
2
NB: Esa línea awk funcionó de manera más confiable para mí que la variante sed (entre OS-X y Ubuntu Linux)
Jay Taylor
3
Tenga en cuenta que esto no elimina nada en el archivo. Simplemente imprime el archivo sin estas líneas en stdout. Por lo tanto, también debe redirigir la salida a un archivo temporal y luego mover el archivo temporal para reemplazar el original.
mivk
17
$ cat foo
1
2
3
4
5
$ sed -e '2d;4d' foo
1
3
5
$ 
Matthew Slattery
fuente
6

Esto es muy a menudo un síntoma de un antipatrón. La herramienta que produjo los números de línea bien puede reemplazarse por una que elimine las líneas de inmediato. Por ejemplo;

grep -nh error logfile | cut -d: -f1 | deletelines logfile

(¿dónde deletelinesestá la utilidad que imagina que necesita?) es lo mismo que

grep -v error logfile

Dicho esto, si se encuentra en una situación en la que realmente necesita realizar esta tarea, puede generar un sedscript simple a partir del archivo de números de línea. Con humor (pero quizás un poco confuso) puedes hacer esto sed.

sed 's%$%d%' linenumbers

Esto acepta un archivo de números de línea, uno por línea, y produce, en la salida estándar, los mismos números de línea dañadidos después de cada uno. Este es un sedscript válido , que podemos guardar en un archivo, o (en algunas plataformas) canalizar a otra sedinstancia:

sed 's%$%d%' linenumbers | sed -f - logfile

En algunas plataformas, sed -fno entiende el argumento de la opción -que significa entrada estándar, por lo que debe redirigir el script a un archivo temporal y limpiarlo cuando haya terminado, o tal vez reemplazar el guión solitario con /dev/stdino /proc/$pid/fd/1si su sistema operativo (o shell ) tiene eso.

Como siempre, puede agregar -iantes la -fopción de sededitar el archivo de destino en lugar de producir el resultado en la salida estándar. En las plataformas * BSDish (incluido OSX) también debe proporcionar un argumento explícito -i; un idioma común es proporcionar un argumento vacío; -i ''.

tripleee
fuente
No estoy del todo de acuerdo con el "síntoma de un antipatrón". Los tipos de archivos basados ​​en marcas (por ejemplo, XML o JSON) requieren líneas específicas al final para ser archivos válidos. En ese caso, a menudo es el enfoque más razonable para eliminar esas líneas, poner en el archivo lo que desea agregar y luego volver a agregar esas líneas, porque poner las líneas en el medio de inmediato puede ser mucho más esfuerzo, y va en contra el deseo potencial de evitar herramientas adicionales como sed tanto como puedas.
Egor Hans
No entiendo qué tipo de escenario te estás imaginando. No son escenarios en los que este es un enfoque legítimo, pero la gran mayoría de los casos que he visto son novatos que hacen más o menos exactamente lo que demuestra mi primer ejemplo. (Tal vez provenir de algún lenguaje muy bajo nivel y se utilizan para dividir su camino más allá del problema a nivel molecular, ya que hay que en ASM o C)
tripleee
Eliminar cosas por número de línea de XML o JSON suena extremadamente frágil, si no completamente peligroso.
tripleee
Lo que básicamente quiero decir con eso es que, como creador de dicho archivo, sabes lo que tiene que estar al final del documento (es decir, el conjunto de llaves de cierre / corchetes en las últimas líneas para JSON, o la exacta etiquetas de cierre para XML). Teniendo en cuenta eso, el enfoque más simple para extender dicho documento es 1) eliminar las últimas líneas, 2) agregar el nuevo contenido, 3) volver a agregar las últimas líneas. De esta forma, el documento puede ser válido tanto antes como después de que se haya extendido, sin necesidad de encontrar una forma de agregar líneas a mitad del documento.
Egor Hans
1
Hasta ahora, esta es la única respuesta con una solución adecuada para una gran cantidad de líneas (es decir, proporcionada por un archivo). Y el prólogo también tiene sentido. Se merece más votos a favor. Por cierto, si desea imprimir líneas en lugar de eliminarlas, use en plugar de d, junto con la opción -n(no funcionará sin -n, y !dtampoco funcionará).
Skippy le Grand Gourou
2

Me gustaría proponer una generalización con awk.

Cuando el archivo está hecho por bloques de un tamaño fijo y las líneas para eliminar se repiten para cada bloque, awk puede funcionar bien de tal manera

awk '{nl=((NR-1)%2000)+1; if ( (nl<714) || ((nl>1025)&&(nl<1029)) ) print  $0}'
 OriginFile.dat > MyOutputCuttedFile.dat

En este ejemplo, el tamaño del bloque es 2000 y quiero imprimir las líneas [1..713] y [1026..1029].

  • NR es la variable utilizada por awk para almacenar el número de línea actual.
  • % da el resto (o módulo) de la división de dos enteros;
  • nl=((NR-1)%BLOCKSIZE)+1Aquí escribimos en la variable nl el número de línea dentro del bloque actual. (vea abajo)
  • ||y &&son el operador lógico OR y AND .
  • print $0 escribe la línea completa

Why ((NR-1)%BLOCKSIZE)+1:
(NR-1) We need a shift of one because 1%3=1, 2%3=2, but 3%3=0.
  +1   We add again 1 because we want to restore the desired order.

+-----+------+----------+------------+
| NR  | NR%3 | (NR-1)%3 | (NR-1)%3+1 |
+-----+------+----------+------------+
|  1  |  1   |    0     |     1      |
|  2  |  2   |    1     |     2      |
|  3  |  0   |    2     |     3      |
|  4  |  1   |    0     |     1      |
+-----+------+----------+------------+

Hastur
fuente
2
Admiro la forma en que haces honor a tu nombre inductor de locura.
Jukka Dahlbom