Hay dos formas de interpretar esta pregunta; Abordaré ambos casos. Es posible que desee mostrar líneas:
- que contienen una secuencia de cuatro dígitos que en sí misma no forma parte de una secuencia de dígitos más larga, o
- que contiene una secuencia de cuatro dígitos pero ya no es una secuencia de dígitos (ni siquiera por separado).
Por ejemplo, (1) se mostraría 1234a56789
, pero (2) no.
Si desea mostrar todas las líneas que contienen una secuencia de cuatro dígitos que no es parte de una secuencia de dígitos más larga, una forma es:
grep -P '(?<!\d)\d{4}(?!\d)' file
Esto utiliza expresiones regulares de Perl , que son compatibles con Ubuntu grep
( GNU grep ) -P
. No coincidirá con el texto como 12345
, ni coincidirá con el 1234
o 2345
que son parte de él. Pero va a coincidir con el 1234
de 1234a56789
.
En expresiones regulares de Perl:
\d
significa cualquier dígito (es una forma corta de decir [0-9]
o [[:digit:]]
).
x{4}
coincide x
4 veces. (la {
}
sintaxis no es específica de las expresiones regulares de Perl; también está en expresiones regulares extendidas a través de grep -E
). Así \d{4}
es lo mismo que \d\d\d\d
.
(?<!\d)
es una afirmación negativa de ancho cero. Significa "a menos que sea precedido por \d
".
(?!\d)
es una afirmación negativa anticipada de ancho cero. Significa "a menos que sea seguido por \d
".
(?<!\d)
y (?!\d)
no coincide con el texto fuera de la secuencia de cuatro dígitos; en cambio, (cuando se usan juntos) evitarán que una secuencia de cuatro dígitos coincida si es parte de una secuencia más larga de dígitos.
Usar solo el mirar hacia atrás o solo mirar hacia adelante es insuficiente porque la subsecuencia de cuatro dígitos más a la derecha o más a la izquierda aún coincidiría.
Una ventaja de usar las afirmaciones de mirar hacia atrás y mirar hacia adelante es que su patrón solo coincide con las secuencias de cuatro dígitos, y no con el texto circundante. Esto es útil cuando se utiliza el resaltado de color (con la --color
opción).
ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4
Por defecto en Ubuntu, cada usuario tiene alias grep='grep --color=auto'
en su ~.bashrc
archivo . Por lo tanto, obtiene el resaltado de color automáticamente cuando ejecuta un comando simple que comienza con grep
(esto es cuando se expanden los alias ) y la salida estándar es un terminal (esto es lo que se verifica). Las coincidencias generalmente se resaltan en un tono rojo (cerca de bermellón ), pero lo he mostrado en negrita en cursiva. Aquí hay una captura de pantalla:--color=auto
E incluso puede grep
imprimir solo texto coincidente, y no toda la línea, con -o
:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
De forma alternativa, sin afirmaciones de mirar atrás y mirar atrás
Sin embargo, si usted:
- necesita un comando que también se ejecute en sistemas en los
grep
que no sea compatible -P
o no quiera usar una expresión regular de Perl, y
- no es necesario que coincida específicamente con los cuatro dígitos, lo cual suele ser el caso si su objetivo es simplemente mostrar líneas que contengan coincidencias, y
- están de acuerdo con una solución que es un poco menos elegante
... entonces puede lograr esto con una expresión regular extendida en su lugar:
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
Esto coincide con cuatro dígitos y el carácter sin dígitos, o el principio o el final de la línea, que los rodea. Específicamente:
[0-9]
coincide con cualquier dígito (como [[:digit:]]
, o \d
en expresiones regulares de Perl) y {4}
significa "cuatro veces". Entonces [0-9]{4}
coincide con una secuencia de cuatro dígitos.
[^0-9]
coincide con caracteres que no están en el rango de 0
hasta 9
. Es equivalente a [^[:digit:]]
(o \D
, en expresiones regulares de Perl).
^
, cuando no aparece entre [
]
paréntesis, coincide con el comienzo de una línea. Del mismo modo, $
coincide con el final de una línea.
|
medios o y paréntesis son para agrupar (como en álgebra). Por lo tanto, (^|[^0-9])
coincide con el comienzo de la línea o un carácter sin dígitos, mientras que ($|[^0-9])
coincide con el final de la línea o con un carácter sin dígitos.
Entonces, las coincidencias ocurren solo en líneas que contienen una secuencia de cuatro dígitos ( [0-9]{4}
) que es simultáneamente:
- al comienzo de la línea o precedido por un no dígito (
(^|[^0-9])
), y
- al final de la línea o seguido de un no dígito (
($|[^0-9])
).
Si, por otro lado, desea mostrar todas las líneas que contienen una secuencia de cuatro dígitos, pero no contiene ninguna secuencia de más de cuatro dígitos (incluso una que esté separada de otra secuencia de solo cuatro dígitos), entonces conceptualmente su El objetivo es encontrar líneas que coincidan con un patrón pero no con otro.
Por lo tanto, incluso si sabe cómo hacerlo con un solo patrón, le sugiero que use algo como la segunda sugerencia de grep
Matt , para los dos patrones por separado.
Al hacerlo, no se beneficia mucho de ninguna de las características avanzadas de las expresiones regulares de Perl, por lo que es posible que prefiera no usarlas. Pero de acuerdo con el estilo anterior, aquí hay una reducción de la solución de Matt usando \d
(y llaves) en lugar de [0-9]
:
grep -P '\d{4}' file | grep -Pv '\d{5}'
Como utiliza [0-9]
, Matt's es más portátil: funcionará en sistemas donde grep
no admite expresiones regulares de Perl. Si usa [0-9]
(o [[:digit:]]
) en lugar de \d
, pero continúa usándolo {
}
, obtendrá la portabilidad de Matt's de manera un poco más concisa:
grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'
De forma alternativa, con un solo patrón
Si realmente prefieres un grep
comando que
- usa una sola expresión regular (no dos
grep
s separadas por una tubería , como se indicó anteriormente)
- para mostrar líneas que contienen al menos una secuencia de cuatro dígitos,
- pero sin secuencias de cinco (o más) dígitos,
- y no te importa hacer coincidir toda la línea, no solo los dígitos (probablemente no te importe esto)
... entonces puedes usar:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file
El -x
indicador hace que se grep
muestren solo las líneas donde coincide la línea completa (en lugar de cualquier línea que contiene una coincidencia).
He usado una expresión regular de Perl porque creo que la brevedad \d
y la \D
claridad sustancialmente aumentan en este caso. Pero si necesita algo portátil para sistemas donde grep
no es compatible -P
, puede reemplazarlos con [0-9]
y [^0-9]
(o con [[:digit:]]
y [^[:digit]]
):
grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file
La forma en que funcionan estas expresiones regulares es:
En el medio, \d{4}
o [0-9]{4}
coincide con una secuencia de cuatro dígitos. Es posible que tengamos más de uno de estos, pero necesitamos tener al menos uno.
A la izquierda, (\d{0,4}\D)*
o ([0-9]{0,4}[^0-9])*
coincide con cero o más ( *
) instancias de no más de cuatro dígitos seguidos de un no dígito. Cero dígitos (es decir, nada) es una posibilidad para "no más de cuatro dígitos". Esto coincide con (a) la cadena vacía o (b) cualquier cadena que termine en un no dígito y que no contenga ninguna secuencia de más de cuatro dígitos.
Dado que el texto inmediatamente a la izquierda de la central \d{4}
(o [0-9]{4}
) debe estar vacío o terminar con un no dígito, esto evita que la central \d{4}
coincida con cuatro dígitos que tienen otro (quinto) dígito justo a la izquierda de ellos.
A la derecha, (\D\d{0,4})*
o ([^0-9][0-9]{0,4})*
coincide con cero o más ( *
) instancias de un no dígito seguido de no más de cuatro dígitos (que, como antes, podrían ser cuatro, tres, dos, uno o incluso ninguno). Esto coincide con (a) la cadena vacía o (b) cualquier cadena que comience en un no dígito y que no contenga ninguna secuencia de más de cuatro dígitos.
Dado que el texto inmediatamente a la derecha de la central \d{4}
(o [0-9]{4}
) debe estar vacío o comenzar con un no dígito, esto evita que la central \d{4}
coincida con cuatro dígitos que tienen otro (quinto) dígito justo a la derecha de ellos.
Esto asegura que una secuencia de cuatro dígitos esté presente en alguna parte, y que ninguna secuencia de cinco o más dígitos esté presente en ninguna parte.
No está mal ni está mal hacerlo de esta manera. Pero quizás la razón más importante para considerar esta alternativa es que aclara el beneficio de usar (o similar) en su lugar, como se sugirió anteriormente y en la respuesta de Matt .grep -P '\d{4}' file | grep -Pv '\d{5}'
De esa manera, queda claro que su objetivo es seleccionar líneas que contengan una cosa pero no otra. Además, la sintaxis es más simple (por lo que muchos lectores / mantenedores pueden entenderla más rápidamente).
1234a12345
mostrarse una línea como , o no?