¿Cuál es el punto de usar múltiples signos de exclamación en sed?

12

La documentación de POSIX sed decía:

Una función puede estar precedida por uno o más '!' caracteres, en cuyo caso la función se aplicará si las direcciones no seleccionan el espacio del patrón. Se aceptarán cero o más caracteres <en blanco> antes del primer '!' personaje. No se especifica si los caracteres <en blanco> pueden seguir a '!' carácter, y las aplicaciones conformes no seguirán un '!' carácter con caracteres <en blanco>.

Entonces, con cualquier POSIX sed, podemos:

sed -e '/pattern/!d' file

Es lo mismo que escribir:

sed -e '/pattern/!!d' file

Y !!!dy nde los signos de exclamación todavía están bien (Probado con tres sedversiones de heirloom toolchest ). No veo ningún beneficio entre múltiples en lugar de una exclamación.

¿Por qué la especificación permitió esa sintaxis y cómo es útil en la aplicación del mundo real?


Parece que GNU sed no es compatible en este caso, se quejará si usamos múltiples exclamaciones:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s
Cuonglm
fuente
2
FWIW: en OpenBSD !actúa como un conmutador, /pattern/!!es lo mismo /pattern/y /pattern/!!!es lo mismo que /pattern/!. En FreeBSD, múltiples !son lo mismo que uno solo.
lcd047
2
El punto de muchas cosas en la especificación es que sedse pueden generar scripts . Dado un POSIX sed, debería ser realmente sencillo escribir la escritura de una sedsecuencia de comandos. Entonces, si tuvo algún desencadenante para algún caso que debería marcar una dirección !no digna de lo que fue su acción, incluso podría desencadenar eso varias veces para el mismo y aún así obtener los mismos resultados.
mikeserv
@cuonglm No, solo FreeBSD es. Los GNU, OpenBSD y NetBSD sedno lo son.
lcd047
@ lcd047: sí, por supuesto. Perdón por mi mal ingles. Quiero decir que no cumple, ¿verdad? Es bueno saber eso. Pero el punto principal de mi pregunta es cómo esa sintaxis puede ser útil en el mundo real, con POSIX sed.
cuonglm
1
FWIW: se ha comprometido una solución para esto en OpenBSD-current.
lcd047

Respuestas:

5

sedLa API es primitiva, y esto es por diseño. Al menos, se ha mantenido primitivo por diseño, no puedo decir si fue diseñado primitivamente desde el principio. En la mayoría de los casos, la escritura de un sedscript que, cuando se ejecuta, generará otro sedscript es una cuestión simple. sedMuy a menudo se aplica de esta manera por preprocesadores macro como m4y / o make.

(Lo que sigue es un caso de uso altamente hipotético: es un problema diseñado para adaptarse a una solución. Si se siente como una exageración para usted, entonces probablemente sea porque sí, pero eso no necesariamente lo hace menos válido).


Considere el siguiente archivo de entrada:

cat <<"" >./infile
camel
cat dog camel
dog cat
switch
upper
lower

Si quisiéramos escribir una sedsecuencia de comandos que anexaría el caso de palabras al final de cada palabra apropiada en el archivo de entrada anterior solo si pudiera encontrarse en una línea en el contexto apropiado , y deseamos hacerlo de la manera más eficiente posible ( como debería ser nuestro objetivo, por ejemplo, durante una operación de compilación), entonces deberíamos preferir evitar aplicar /expresiones regulares /tanto como sea posible.

Una cosa que podríamos hacer es editar previamente el archivo en nuestro sistema en este momento, y nunca llamar seden absoluto durante la compilación. Pero si alguna de esas palabras en el archivo se incluye o no en función de la configuración local y / o las opciones de tiempo de compilación, es probable que hacerlo no sea una alternativa deseable.

Otra cosa que podríamos hacer es procesar el archivo ahora contra expresiones regulares. Podemos producir, e incluir en nuestra compilación, un sedscript que puede aplicar ediciones según el número de línea, que suele ser una ruta mucho más eficiente a largo plazo.

Por ejemplo:

n=$(printf '\\\n\t')
grep -En 'camel|upper|lower' <infile |
sed "   1i${n%?}#!/usr/heirloom/bin/posix2001/sed -nf
        s/[^:]*/:&$n&!n;&!b&$n&/;s/://2;\$a${n%?}q"'
        s/ *cat/!/g;s/ *dog/!/g
        s| *\([cul][^ ]*\).*|s/.*/\1-case/p|'

... que escribe la salida en forma de sedscript y que se parece a ...

#!/usr/heirloom/bin/posix2001/sed -nf
:1
    1!n;1!b1
    1s/.*/camel-case/p
:2
    2!n;2!b2
    2!!s/.*/camel-case/p
:5
    5!n;5!b5
    5s/.*/upper-case/p
:6
    6!n;6!b6
    6s/.*/lower-case/p
q

Cuando esa salida se guarda en un archivo de texto ejecutable en mi máquina con el nombre ./bang.sedy se ejecuta como ./bang.sed ./infile, la salida es:

camel-case
upper-case
lower-case

Ahora podrías preguntarme ... ¿Por qué querría hacer eso? ¿Por qué no simplemente anclar greplos partidos? ¿Quién usa camel-case de todos modos? Y a cada pregunta que solo podía responder, no tengo idea ... porque no. ¡Antes de leer esta pregunta, nunca había notado personalmente el multi-! requisito de análisis en la especificación: creo que es una buena captura.

El multi-! Sin embargo, esto inmediatamente tenía sentido para mí: gran parte de la sedespecificación está orientada a scripts simplemente analizados y simplemente generados sed . Probablemente encontrará los \ndelimitadores de línea electrónica necesarios para [wr:bt{]tener mucho más sentido en ese contexto, y si tiene en cuenta esa idea, podría tener un mejor sentido de algunos otros aspectos de la especificación (como :no aceptar direcciones y qnegarse a aceptar más de 1) .

En el ejemplo anterior, escribo una cierta forma de sedscript que solo se puede leer una vez. Si lo mira detenidamente, puede notar que a medida que sedlee el archivo de edición, progresa de un bloque de comandos al siguiente: nunca se bifurca o completa su script de edición hasta que haya terminado completamente con su archivo de edición.

Considero que multi-! las direcciones pueden ser más útiles en ese contexto que en algunos otros, pero, sinceramente, no puedo pensar en un solo caso en el que podría haberle dado un uso muy bueno, y yo sedmucho. También creo que es digno de mención que sedambos GNU / BSD no logran manejarlo como se especifica: probablemente este no sea un aspecto de la especificación que tenga mucha demanda, por lo que si una implementación lo pasa por alto, dudo mucho que sus errores @ box sufran terriblemente como resultado.

Dicho esto, no manejar esto como se especifica es un error para cualquier implementación que pretenda cumplir, por lo que creo que enviar un correo electrónico a los cuadros de desarrollo relevantes se requiere aquí, y tengo la intención de hacerlo si no lo hace.

mikeserv
fuente
1
Ahora está arreglado en OpenBSD-current.
lcd047
1
Múltiple !se eliminará en la próxima especificación , ¡qué está pasando aquí!
Cuonglm
@cuonglm: demasiado poco, demasiado tarde, supongo. tal vez estaba más cerca de la marca de lo que pensaba.
mikeserv
@cuonglm: bueno, está bien, pero ¿qué significa eso ... aceptado como marcado ?
mikeserv
1
@mikeserv: la respuesta explicó mi asombro y me dio otra vista con sed API. ¡Tiene sentido para mí!
Cuonglm