Grep eliminar línea con 0 pero no 0.2?

12

Tengo un archivo cuyo contenido es similar al siguiente.

0
0
0.2
0
0
0
0

Necesito eliminar todas las líneas con un solo cero.
Estaba pensando en usar grep -v "0", pero esto también elimina la línea que contiene 0.2. Vi que podía usar la -wopción, pero esto tampoco parece funcionar.

¿Cómo puedo eliminar todas las líneas que contienen un solo 0 y mantener todas esas líneas que comienzan con un 0?

Philip Kirkbride
fuente
2
Posible duplicado de la cadena exacta
Julien Lopez
1
@JulienLopez No es un engaño de esa pregunta. Esa pregunta se trata de hacer coincidir una palabra y responder con -w, que falla aquí.
Sparhawk
¿Por qué estás obligado a usar greppara esta tarea? ¿Y qué quieres decir exactamente con un solo cero ? Esto suena muy parecido a un problema XY .
Roland Illig
1
@RolandIllig fue 1 hora antes de acostarse y quería comenzar a procesar una serie de 500,000 cadenas para verificar si eran claves privadas de bitcoin y, de ser así, obtener el saldo. La próxima vez que tuve tiempo de verlo, procesé muchos miles de cadenas y solo quería analizar cualquier valor que no sea cero.
Philip Kirkbride

Respuestas:

35
grep -vx 0

De man grep:

-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 $.

-wfalla porque el primero 0en 0.02se considera una "palabra", y por lo tanto se corresponde esta línea. Esto se debe a que es seguido por un carácter "sin palabra". Puede ver esto si ejecuta el comando original sin -v, es decir grep -w "0".

Gavilán
fuente
También podría usar la -Fopción ya que no estamos usando patrones de expresiones regulares, solo coincidencias de cadenas simples
glenn jackman
@glennjackman Tal vez he leído esto antes, pero parece que no puedo encontrarlo ahora. Correr con -F(sorprendentemente para mí) parece tomar una cantidad de tiempo similar o incluso un poco más lento (~ 5–10%). Por lo tanto, no estoy seguro de cuál sería la ventaja.
Sparhawk
2
Es posible que el motor RegEx se use con tanta frecuencia y tan ampliamente que hayan implementado una versión muy eficiente del mismo, pero que una "búsqueda simple" probablemente no se haya actualizado durante 30 años.
Nelson
@Sparhawk: greppresumiblemente tiene un caso especial para expresiones regulares sin metacaracteres, porque ese es un caso de uso común. Es sorprendente que fgrepsea ​​más lento, pero no es sorprendente que la sobrecarga de notar este caso especial al compilar un patrón corto sea insignificante en comparación con el tiempo para escanear un archivo grande. (Si requiere un caso especial para ir tan rápido, contra un patrón con una clase de personaje o x.*y.)
Peter Cordes
Pero eso es quizás una simplificación excesiva porque la entrada es en realidad muchas líneas cortas (no una cadena gigante). Olvidé si grepreconoce cualquier carácter que no sea \nnueva línea como separador de línea. Si no, lo implícito ^y $ aún puede convertirse en una búsqueda de cadena fija como strstr(big_buf, "\n0\n"). (O 0\nal comienzo de un búfer). Pero no solo estamos buscando la primera coincidencia potencialmente lejos en un gran búfer, queremos filtrar eficientemente. Pero de todos modos, en teoría sí, es solo un memcmp de 2 bytes al comienzo de cada línea, y es de esperar que tanto fgrep como grep lo vean.
Peter Cordes
28

Con grep:

grep -v "^0$" file

^significa el comienzo de la línea, $significa el final de la línea.

Arkadiusz Drabczyk
fuente
2
Esto es lo que solicitó el usuario: evite las líneas que contengan solo 1 "0".
Olivier Dulac
1
No pondría un signo de dólar literal entre comillas dobles como esa.
user541686
@mehrdad no es un gran problema con la expresión regular, ya que generalmente es el último char o el siguiente no será[a-Z0-9]
Sampo Sarrala - codidact.org
14

Si bien grep se puede usar para esto (como muestran claramente otras respuestas), demos un paso atrás y pensemos en lo que realmente quieres:

  • Tienes un archivo que contiene números
  • Desea realizar un filtrado basado en el valor numérico .

Regex interpreta datos de secuencia de caracteres. No conocen los números, solo los dígitos individuales (y sus combinaciones regulares). Aunque en su caso particular hay un simple truco en torno a esta limitación, en última instancia es un desajuste de requisitos.

A menos que haya una muy buena razón para usar grepaquí (por ejemplo, porque lo ha medido y es mucho más eficiente, y la eficiencia es crucial en su caso), le recomiendo usar una herramienta diferente.

awk, por ejemplo, puede filtrar en base a comparaciones numéricas, por ejemplo:

awk '$1 == 0' your_file

Pero también, para obtener todas las líneas que contienen números mayores que cero:

awk '$1 > 0' your_file

Me encanta la expresión regular, es una gran herramienta. Pero no es la única herramienta. Como dice el refrán, si todo lo que tienes es grep, todo parece un lenguaje normal.

Konrad Rudolph
fuente
3
Estoy totalmente de acuerdo en que awk puede ser más elegante aquí ... sin embargo, también coincidirá quizás un poco más de lo que el usuario espera (cada valor numérico se evalúa a 0). Es decir, printf '0\n1\n-1\na\nb\n0\n0 also\n0.0\n-0.0\n0*0\n' | awk '($1 == 0)'va a coincidir: 0, 0.0y -0.0... y también 0 also! No solo "0". (que a veces es lo que se necesita, a veces no). Si el usuario solo quiere "0": awk '/^0$/' (o grep '^0$'). También debe editar: el usuario debe agregar !para negar la prueba, por lo que se oculta 0(y otros ceros) y muestra el resto. es decir:awk '!( $0 == 0)'
Olivier Dulac
1
@Olivier, o verifique el valor de la cadena:$1 == "0"
Glenn Jackman
1
@OlivierDulac Utilicé explícitamente en >lugar de !=(o, equivalentemente ! (… == …)) , para resaltar que esta es una comparación numérica arbitraria, no solo la igualdad. En cuanto a su otro comentario, esto es completamente cierto, pero luego estamos esencialmente de vuelta en el territorio de comparación de cadenas y la solución existente usando greptrabajos (aunque, awkpor supuesto, también funciona).
Konrad Rudolph
@KonradRudolph puntos justos :)
Olivier Dulac
1
@glennjackman: buen truco de hecho. Pero entonces OP preferiría hacer la prueba$0=="0"
Olivier Dulac
5

grep's -wes un poco enrevesado de una manera que divide la cadena original en componentes de palabras y no palabras (cualquier cosa excepto letras, dígitos o guiones bajos). Puesto que ya se ha encontrado con aa constituyente palabra válida 0en la 0.02que había afirmado la lógica de la negación para eliminar la línea.

Usar sedes un poco fácil en este contexto para eliminar las palabras completas que coinciden

sed '/^0$/d' file
Inian
fuente
3

Cuando las líneas que desea eliminar solo contienen un 0 seguido de la siguiente línea , puede seleccionar esas líneas emitiendo el siguiente comando:

grep -v "^0$"

Esto solo imprimirá las ocurrencias 0que se encuentran al final de una línea y al principio de una línea al mismo tiempo. La -vopción luego invierte nuestra selección.

majesticLSD
fuente
1
Esta respuesta es casi idéntica a la de Arkadiusz Drabczyk, pero olvidó la -v, por lo que no funciona.
Sparhawk
Tienes razón. Estaba escribiendo mientras él publicaba su respuesta, así que no vi que ya se había dado. He leído mal esa parte con la -vopción, ¡gracias!
majesticLSD
0
  • \ b - borde de la palabra

grep -v "\b0\b"

  • coincide con el comienzo de la línea, su patrón y el final de la línea

grep -v "^0$"

  • o como @Sparhawk sugirió -vx lineregexp

-w funciona, pero en su caso 0.2 son dos palabras porque el carácter de punto es un separador de palabras.

Jakub Jindra
fuente
grep -v "\b0\b"Realmente no funciona aquí. ¿Qué versión de grep usas?
Arkadiusz Drabczyk
funciona con grep (BSD grep) 2.5.1-FreeBSDmacOS y grep (GNU grep) 2.16ubuntu
Jakub Jindra
1
Uso de expresiones regulares de GNU \<y \>como límites de palabras, pero eso tendrá el mismo efecto que-w
Glenn Jackman
0

Otra respuesta por el bien de la variedad, suponiendo que tenga un PCRE habilitado grep

grep -Pv "^0(?!\.)"

Esto lleva a cabo una búsqueda anticipada negativa para que coincida con las líneas que comienzan 0y no son seguidas por un punto. Luego -vdescarta las líneas que no coinciden. Puedes ver en acción aquí

mrbolichi
fuente
1
Esto también eliminará líneas como 0123, que no es lo que el OP quiere
iruvar
0

Suponiendo que cualquier línea que no sea solo un 0 tenga un punto

grep '\.' file

Roger Mungo
fuente