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 y sed
encuentra EOF a través - termina el procesamiento. Entonces, si está en la última línea y le indica sed
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
y ex
, por ejemplo, puede hacer mucho de lo que sed
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 salida sed
, 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 que sed
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 con y///
. Si a menudo te encuentras g
sustituyendo a nivel mundial un solo personaje por otro, entonces y
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 :
La POSIXLY_CORRECT
variable de entorno se menciona porque POSIX especifica que si sed
encuentra EOF al intentarlo N
, 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 ...
:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...
... es muy probable que funcionen de manera errática dependiendo de la sed
implementación que los lea. Portablemente deben escribirse:
...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}
Lo mismo es cierto para r
, w
, t
, a
, i
, y c
(y, posiblemente, un poco más que yo estoy olvidando por el momento) . En casi todos los casos, también podrían escribirse:
sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
"//{ do arbitrary list of commands" -e \}
... donde la nueva -e
instrucción xecution representa el \n
delimitador de la línea ew. Entonces, cuando el info
texto de GNU sugiere que una implementación tradicional sed
lo obligaría a hacer :
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
... más bien debería ser ...
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}
... 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:
printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
//!g;x;$!d;:nd' -e 'l;$a\' \
-e 'this is the last line'
... que imprime:
foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line
... porque el t
comando est, como la mayoría de los sed
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 el h
espacio antiguo. Los búferes se x
cambian a continuación, y s///
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 tiene t
éxito, el guión se ramifica a la etiqueta n
ot d
elete, que hace un l
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ómo sed
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 y n
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 el sed
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:
sed 'H;1h;$!d;x;...'
... que reunirá todo el archivo o lo intentará.
una nota al margen sobre N
el comportamiento de la última línea ...
Si bien no tengo las herramientas disponibles para probar, tenga en cuenta que N
cuando la lectura y la edición in situ se comportan de manera diferente si el archivo editado es el archivo de script para la próxima lectura.