Leer un archivo completo en el espacio de patrones es útil para sustituir nuevas líneas, & c. y hay muchas instancias que aconsejan lo siguiente:
sed ':a;N;$!ba; [commands...]'
Sin embargo, falla si la entrada contiene solo una línea.
Como ejemplo, con dos entradas de línea, cada línea está sujeta al comando de sustitución:
$ echo $'abc\ncat' | sed ':a;N;$!ba; s/a/xxx/g'
xxxbc
cxxxt
Pero, con una sola entrada de línea, no se realiza ninguna sustitución:
$ echo 'abc' | sed ':a;N;$!ba; s/a/xxx/g'
abc
¿Cómo se escribe un sed
comando para leer todas las entradas a la vez y no tener este problema?
sed -z
opción de GNU . Si su archivo no tiene nulo, ¡se leerá hasta el final del archivo! Encontrado en esto: stackoverflow.com/a/30049447/582917Respuestas:
Hay todo tipo de razones por las cuales leer un archivo completo en el espacio de patrones puede salir mal. El problema lógico en la pregunta que rodea la última línea es común. Está relacionado con
sed
el ciclo de línea de - cuando no hay más líneas ysed
encuentra EOF a través - termina el procesamiento. Entonces, si está en la última línea y le indicased
que obtenga otra, se detendrá allí y no hará más.Dicho esto, si realmente necesita leer un archivo completo en el espacio de patrones, entonces probablemente valga la pena considerar otra herramienta de todos modos. El hecho es que
sed
es el mismo nombre del editor de flujo , está diseñado para trabajar una línea, o un bloque de datos lógico, a la vez.Hay muchas herramientas similares que están mejor equipadas para manejar bloques de archivos completos.
ed
yex
, por ejemplo, puede hacer mucho de lo quesed
puede hacer y con una sintaxis similar, y mucho más, pero en lugar de operar solo en una secuencia de entrada mientras se transforma en salidased
, también mantienen archivos de respaldo temporales en el sistema de archivos . Su trabajo se almacena en el disco según sea necesario, y no se cierra abruptamente al final del archivo (y tiende a explotar con mucha menos frecuencia bajo la tensión del búfer) . Además, ofrecen muchas funciones útiles quesed
no lo hacen, del tipo que simplemente no tiene sentido en un contexto de flujo, como marcas de línea, deshacer, búferes con nombre, unirse y más.sed
La fortaleza principal es su capacidad para procesar datos tan pronto como los lee, de manera rápida, eficiente y en tiempo real. Cuando sorbe un archivo, lo tira y tiende a encontrarse con dificultades de caso límite como el problema de la última línea que menciona, desbordamientos de búfer y rendimiento abismal, a medida que los datos que analiza crecen en longitud el tiempo de procesamiento de un motor de expresiones regulares al enumerar coincidencias aumenta exponencialmente .Con respecto a este último punto, por cierto: si bien entiendo que el
s/a/A/g
caso de ejemplo es muy probable que sea solo un ejemplo ingenuo y probablemente no sea el guión real para el que desea recopilar una entrada, es posible que valga la pena familiarizarse cony///
. Si a menudo te encuentrasg
sustituyendo a nivel mundial un solo personaje por otro, entoncesy
podría ser muy útil para ti. Es una transformación en lugar de una sustitución y es mucho más rápido, ya que no implica una expresión regular. Este último punto también puede ser útil cuando se intenta preservar y repetir//
direcciones vacías porque no las afecta pero puede verse afectada por ellas. En cualquier caso,y/a/A/
es un medio más simple de lograr lo mismo, y los intercambios también son posibles como:y/aA/Aa/
que intercambiarían todas las mayúsculas / minúsculas como en una línea entre sí.También debe tener en cuenta que el comportamiento que describe realmente no es lo que se supone que debe suceder de todos modos.
De GNU
info sed
en la sección ERRORES COMUNES REPORTADOS :N
comando en la última líneaLa mayoría de las versiones de
sed
exit sin imprimir nada cuando elN
comando se emite en la última línea de un archivo. GNUsed
imprime el espacio del patrón antes de salir a menos que, por supuesto,-n
se haya especificado el interruptor de comando. Esta elección es por diseño.Por ejemplo, el comportamiento de
sed N foo bar
dependería de si foo tiene un número par o impar de líneas. O, al escribir un script para leer las siguientes líneas después de una coincidencia de patrones, las implementaciones tradicionales desed
te obligarían a escribir algo así en/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
lugar de solo/foo/{ N;N;N;N;N;N;N;N;N; }
.En cualquier caso, la solución más simple es usar
$d;N
scripts que se basen en el comportamiento tradicional, o establecer laPOSIXLY_CORRECT
variable en un valor no vacío.La
POSIXLY_CORRECT
variable de entorno se menciona porque POSIX especifica que sised
encuentra EOF al intentarloN
, debe salir sin salida, pero la versión GNU rompe intencionalmente con el estándar en este caso. Tenga en cuenta también que, aunque el comportamiento se justifica por encima de la suposición, es que el caso de error es uno de edición de flujo, no de arrastrar un archivo completo a la memoria.El estándar define
N
el comportamiento de la siguiente manera:N
Agregue la siguiente línea de entrada, menos su línea de
\n
ew final , al espacio del patrón, utilizando una\n
línea de ew incrustada para separar el material adjunto del material original. Tenga en cuenta que el número de línea actual cambia.Si no hay disponible la siguiente línea de entrada, el
N
verbo de comando se bifurcará hasta el final del script y se cerrará sin comenzar un nuevo ciclo o copiar el espacio del patrón a la salida estándar.En esa nota, hay otros GNU-ismos demostrados en la pregunta, particularmente el uso de la
:
etiqueta,b
rancho y{
corchetes de contexto de función}
. Como regla general,sed
se entiende que cualquier comando que acepte un parámetro arbitrario se delimita en una línea\n
electrónica en el script. Entonces los comandos ...... es muy probable que funcionen de manera errática dependiendo de la
sed
implementación que los lea. Portablemente deben escribirse:Lo mismo es cierto para
r
,w
,t
,a
,i
, yc
(y, posiblemente, un poco más que yo estoy olvidando por el momento) . En casi todos los casos, también podrían escribirse:... donde la nueva
-e
instrucción xecution representa el\n
delimitador de la línea ew. Entonces, cuando elinfo
texto de GNU sugiere que una implementación tradicionalsed
lo obligaría a hacer :... más bien debería ser ...
... por supuesto, eso tampoco es cierto. Escribir el guión de esa manera es un poco tonto. Hay medios mucho más simples para hacer lo mismo, como:
... que imprime:
... porque el
t
comando est, como la mayoría de lossed
comandos, depende del ciclo de línea para actualizar su registro de retorno y aquí el ciclo de línea puede realizar la mayor parte del trabajo. Esa es otra compensación que realiza cuando sorbe un archivo: el ciclo de la línea no se actualiza nunca más y muchas pruebas se comportarán de manera anormal.El comando anterior no se arriesga a una entrada exagerada porque solo hace algunas pruebas simples para verificar lo que lee mientras lo lee. Con
H
antiguo, todas las líneas se agregan al espacio de espera, pero si una línea coincide/foo/
, sobrescribe elh
espacio antiguo. Los búferes sex
cambian a continuación, ys///
se intenta una sustitución condicional si el contenido del búfer coincide con el//
último patrón abordado. En otras palabras,//s/\n/&/3p
intenta reemplazar la tercera nueva línea en el espacio de espera consigo mismo e imprime los resultados si el espacio de espera coincide actualmente/foo/
. Si eso tienet
éxito, el guión se ramifica a la etiquetan
otd
elete, que hace unl
ook y termina el guión.Sin
/foo/
embargo, en el caso de que ambas y una tercera línea nueva no puedan coincidir en el espacio de espera,//!g
sobrescribirán el búfer si/foo/
no coincide, o, si coincide, sobrescribirá el búfer si una línea\n
ew no coincide (reemplazando así/foo/
con en sí) . Esta pequeña prueba sutil evita que el búfer se llene innecesariamente durante largos períodos de no/foo/
y garantiza que el proceso se mantenga ágil porque la entrada no se acumula. Continuando en un caso de no/foo/
o//s/\n/&/3p
falla, los buffers se intercambian nuevamente y se eliminan todas las líneas, excepto la última.Esa última, la última línea
$!d
, es una demostración simple de cómosed
se puede hacer un script de arriba hacia abajo para manejar múltiples casos fácilmente. Cuando su método general es eliminar los casos no deseados, comenzando por los más generales y trabajando hacia los más específicos, los casos límite se pueden manejar más fácilmente porque simplemente se les permite llegar hasta el final del script con sus otros datos deseados y cuándo todo se envuelve y te quedan solo los datos que deseas. Sin embargo, tener que recuperar estos casos extremos de un circuito cerrado puede ser mucho más difícil de hacer.Y aquí está lo último que tengo que decir: si realmente debe extraer un archivo completo, entonces puede soportar hacer un poco menos de trabajo confiando en el ciclo de línea para hacerlo por usted. Normalmente se usaría
N
ext yn
extensión de búsqueda hacia delante - debido a que avanzan por delante del ciclo de línea. En lugar de implementar redundantemente un bucle cerrado dentro de un bucle, ya que elsed
ciclo de línea es solo un bucle de lectura simple de todos modos, si su propósito es solo reunir información indiscriminadamente, entonces probablemente sea más fácil de hacer:... que reunirá todo el archivo o lo intentará.
una nota al margen sobre
N
el comportamiento de la última línea ...fuente
H
primero es encantador.:a;$!{N;ba}
como mencioné anteriormente: es más fácil usar la forma estándar a largo plazo cuando intentas ejecutar expresiones regulares en sistemas desconocidos. Pero eso no era realmente lo que quise decir: implementas un ciclo cerrado, no puedes meterte tan fácilmente en el medio cuando lo desees como lo harías, ramificando, recortando datos no deseados y dejando que el ciclo suceda. Es como una cosa de arriba hacia abajo: todo lo quesed
hace es un resultado directo de lo que acaba de hacer. Tal vez lo veas de manera diferente, pero si lo intentas, es posible que el script sea más fácil.Falla porque el
N
comando viene antes de la coincidencia de patrón$!
(no la última línea) y sed se cierra antes de realizar cualquier trabajo:Esto también se puede solucionar fácilmente para que funcione con entrada de una sola línea (y, de hecho, para ser más claro en cualquier caso) simplemente agrupando los comandos
N
yb
después del patrón:Funciona de la siguiente manera:
:a
crear una etiqueta llamada 'a'$!
si no es la última línea, entoncesN
agregue la siguiente línea al espacio del patrón (o salga si no hay una línea siguiente) yba
bifurque (vaya a) la etiqueta 'a'Desafortunadamente, no es portátil (ya que se basa en extensiones GNU), pero la siguiente alternativa (sugerida por @mikeserv) es portátil:
fuente
:a;N;$!ba;
.