one-liner vs script

25

He notado muchas preguntas, respuestas y comentarios que expresan desdén por (y a veces incluso miedo) de escribir guiones en lugar de frases ingeniosas. Entonces, me gustaría saber:

  • ¿Cuándo y por qué debería escribir un guión independiente en lugar de un "one-liner"? ¿O viceversa?

  • ¿Cuáles son los casos de uso y las ventajas y desventajas de ambos?

  • ¿Algunos idiomas (p. Ej., Awk o perl) son más adecuados para las frases simples que otros (p. Ej., Python)? Si es así, ¿por qué?

  • ¿Es solo una cuestión de preferencia personal o hay buenas razones (es decir, objetivas) para escribir una u otra en circunstancias particulares? ¿Cuáles son esas razones?

Definiciones

  • one-liner: cualquier secuencia de comandos escritos o pegados directamente en una línea de comandos de shell . Tuberías menudo implican y / o el uso de lenguajes tales como sed, awk, perl, y / o herramientas como grepo cuto sort.

    Es la ejecución directa en la línea de comandos la característica definitoria: la longitud y el formato son irrelevantes. Un "one-liner" puede estar todo en una línea, o puede tener múltiples líneas (p. Ej. Sh for loop, o código awk o sed incrustado, con avances de línea e indentación para mejorar la legibilidad).

  • script: cualquier secuencia de comandos en cualquier idioma interpretado que se guarde en un archivo y luego se ejecute. Un script puede estar escrito completamente en un idioma, o puede ser un contenedor de script de shell alrededor de varios "one-liners" usando otros idiomas.


Tengo mi propia respuesta (que publicaré más adelante), pero quiero que esto se convierta en un Q&A canónico sobre el tema, no solo mi opinión personal.

cas
fuente
44
Con respecto a Python, generalmente es malo para las líneas simples porque el espacio en blanco es parte de la sintaxis, incluida la sangría y las nuevas líneas . Además, es más detallado y explícito que la mayoría de los otros lenguajes comunes.
wjandrea
2
Cualquier cosa que haya tenido que buscar en Google (algunas expresiones regulares, típicamente) y que pueda reutilizar en alguna forma o variante, guardaré en un archivo de script en la nube (dropbox, google cloud, lo que sea). El comentario contiene las palabras clave que sé que voy a usar cuando las necesite nuevamente. Me ahorra más de 5 minutos volviendo a sopesar mis elecciones entre varias respuestas similares, y evitando dificultades, o construyendo la variante necesaria, ya que no encontré la variante bien escrita que necesitaba.
usuario3445853
3
@wjandrea Para agregar: expresiones regulares. Perl tiene una sintaxis específica para ellos que incluye la operación a realizar, etc. En Python necesita una función de importación y escritura de llamadas con literales de cadena como argumentos que requieren muchos más caracteres. La mayoría de las frases simples utilizan muchas expresiones regulares para manipular el texto, por lo que este es un "gran problema".
Giacomo Alzetta
1
@wjandrea Python no es malo para las frases sencillas, no en el sentido de no poder escribir una. En promedio, he podido transformar guiones de 4-5 líneas en líneas simples. Pero como mencionó, es más explícito que otros lenguajes (en mi humilde opinión, es una de las ventajas de Python). Por lo tanto, pueden obtener un recuento de caracteres doble o triple que decir Perl o awk (que tampoco necesita importar algunas bibliotecas a diferencia de Python), y eso es de lo que la mayoría de las personas se quejan: la longitud de esas frases. Pero, de nuevo, en mi humilde opinión es insignificante discutir sobre el recuento de caracteres; si algo funciona - funciona
Sergiy Kolodyazhnyy

Respuestas:

33

Otra respuesta basada en la experiencia práctica.

Usaría una línea si fuera un código de "descarte" que podría escribir directamente en el indicador. Por ejemplo, podría usar esto:

for h in host1 host2 host3; do printf "%s\t%s\n" "$h" "$(ssh "$h" uptime)"; done

Usaría un script si decidiera que vale la pena guardar el código. En este punto, agregaría una descripción en la parte superior del archivo, probablemente agregaría alguna comprobación de errores, y tal vez incluso lo verificaría en un repositorio de código para el control de versiones. Por ejemplo, si decidiera que verificar el tiempo de actividad de un conjunto de servidores era una función útil que usaría una y otra vez, la línea anterior podría expandirse a esto:

#!/bin/bash
# Check the uptime for each of the known set of hosts
########################################################################
#
hosts=(host1 host2 host3)

for h in "${hosts[@]}"
do
    printf "%s\t" "$h"
    uptime=$(ssh -o ConnectTimeout=5 -n "$h" uptime 2>/dev/null)
    printf "%s\n" "${uptime:-(unreachable)}"
done

Generalizando, se podría decir

  • Un trazador de líneas

    • Código simple (es decir, solo "unas pocas" declaraciones), escrito para un propósito específico único
    • Código que se puede escribir rápida y fácilmente cuando sea necesario
    • Código desechable
  • Guión

    • Código que (probablemente) se usará más de una o dos veces
    • Código complejo que requiere más de "unas pocas" declaraciones
    • Código que deberá ser mantenido por otros
    • Código para ser entendido por otros
    • Código que se ejecutará sin supervisión (por ejemplo, desde cron)

Veo un buen número de preguntas aquí en Unix. SE pide una línea para realizar una tarea en particular. Usando mis ejemplos anteriores, creo que el segundo es mucho más comprensible que el primero y, por lo tanto, los lectores pueden aprender más de él. Una solución se puede derivar fácilmente de la otra, por lo que, en aras de la legibilidad (para futuros lectores), probablemente deberíamos evitar proporcionar código comprimido en una línea para cualquier otra cosa que no sea la solución más trivial.

roaima
fuente
Ese es un buen ejemplo práctico de cómo una línea rápida y sucia puede evolucionar en un script más robusto.
Anthony G - justicia para Monica
Este es un buen comienzo, pero hay otra posibilidad. Tengo algunas cosas útiles para usar más de una o dos veces, pero probablemente no en la misma máquina. Los guardo en un archivo de texto que mantengo abierto en todo momento en la computadora de mi trabajo, para poder copiarlos rápidamente y pegarlos en mi cliente SSH (o un símbolo del sistema en un servidor de Windows, para el caso). Estos no son necesariamente "one liners" ni hago el esfuerzo de guardarlos en archivos como "scripts". Los llamo "recetas".
Monty Harder
@MontyHarder Para tales casos, tiendo a guardarlos como scripts y luego los guardo en un repositorio git. Entonces puedo simplemente instalarlos en cualquier máquina que necesite a través de un simple clon de git. Eso es por trabajo. Para uso personal, nunca escribo código en un archivo de "receta" (no uso fragmentos de github, por ejemplo). Guardo todos mis scripts en un directorio $ HOME / bin que he guardado desde mis días de universidad en 1997 (en las universidades SunOS). Se me ocurrió la idea de la novela Neuromancer, donde tanto el protagonista como el antagonista guardaban guiones / programas personales para usar como armas
slebetman
1
Aunque ciertamente es tangencial a la discusión real, en su secuencia de comandos de ejemplo podría usar set -- host1 host2 host3entonces for h in "$@"(o simplemente for h), lo que haría que la secuencia de comandos sea POSIX portátil. :-)
wchargin
2
Me gusta el punto de hacer que el código sea más legible para OP y para que los espectadores lo aprendan. Tal vez sea mejor hacer ambas cosas en una respuesta, pero me resulta más fácil concatenar un guión frente a deconstruir personalmente un trazador de líneas.
FreeSoftwareServers
10

Escribe un guión cuando:

  • se requiere más código
  • valoras la legibilidad
  • es necesario / útil agregar comentarios, para mostrar lo que hace el código
  • necesitas pasar parámetros
  • desea que el script se ejecute en su propio entorno (variables, etc.)
  • probablemente va a reutilizar / adaptar el código para un propósito más complejo

Escribe una frase cuando:

  • solo se necesita una pequeña cantidad de código
  • desea acceder a las variables definidas en el shell actual
  • necesitas una solución rápida y sucia
dr01
fuente
Por lo general, es fácil modificar las cosas en las que opera una línea. Ese punto de "pasar parámetros" para un script quizás debería ser "desea empaquetarlo para un uso simple más adelante". He escrito "one-liners" que incluyen definir una función de shell y llamarla. Aunque ahora que lo pienso, ese se ha establecido después de varias iteraciones y debería ponerlo en un script.
Peter Cordes
9

Cuando una serie requerida de comandos es bastante corta y / o produce un resultado que podría utilizarse como parte de una tubería más grande o un alias de comando, podría ser una buena línea.

Básicamente, creo que las frases simples suelen ser cosas que un administrador experimentado del sistema, familiarizado tanto con el problema como con los comandos utilizados, podría escribir en el acto, sin pensar demasiado.

Cuando una línea se vuelve excesivamente larga o involucra más que condicionales o bucles muy simples, generalmente es mejor escribirlos como un script de varias líneas o una función de shell, para facilitar la lectura. Además, si es algo que usted escribe para ser utilizado una y otra vez, o algo que será utilizado (y posiblemente solucionado) por otras personas, debe estar dispuesto a pasar el tiempo para escribir la solución en un claro (er), comentó: forma de guión

En Python, la sangría es parte de la sintaxis, por lo que literalmente no puede usar las características del lenguaje en toda su extensión si escribe una línea.

Perl y awk one-liners a menudo tratan de usar el poder bruto de las expresiones regulares. Pero algunos llaman a las expresiones regulares Lenguaje de solo escritura, no del todo sin razón. Escribir un guión de varias líneas le permite escribir comentarios para describir lo que se supone que está haciendo una expresión regular en particular, lo que será muy útil cuando vuelva a mirar el guión, después de hacer otras cosas durante seis meses.

Esta es una pregunta inherentemente muy basada en la opinión, ya que implica medir qué cantidad de complejidad es aceptable y compensaciones entre legibilidad y compacidad; Todos estos son asuntos de juicio personal. Incluso la elección de un idioma puede depender de asuntos personales: si un idioma en particular sería teóricamente óptimo para un problema en particular, pero no está familiarizado con él y ya sabe cómo resolver el problema con otro idioma, puede elegir el lenguaje familiar para hacer el trabajo rápidamente y con el mínimo esfuerzo, aunque la solución podría ser técnicamente algo ineficiente.

Lo mejor es a menudo enemigo de lo suficientemente bueno , y esto se aplica mucho aquí.

Y si está familiarizado con Perl, es posible que ya haya oído hablar de TMTOWTDI: hay más de una forma de hacerlo.

telcoM
fuente
más uno por mencionar "o función de shell"
Glenn Jackman
7

Tenga en cuenta que esta es una opinión personal; Tómelo con un grano de sal.

  1. Si el comando se ajusta dentro de, digamos, 80 caracteres, use una línea. Las líneas de comando largas son difíciles de trabajar. También está el problema de la recurrencia, ver # 2

  2. Si necesita ejecutar los comandos más de una vez, use un script. De lo contrario, use una línea, siempre que se cumpla la condición n. ° 1.

  3. Esto es muy subjetivo, pero sí, veo que Shell, Perl o awk son mis herramientas de referencia para frases sencillas.
  4. Ver # 1, # 2, # 3.
schaiba
fuente
3
Me gustan las respuestas que he visto hasta ahora. Me gusta especialmente su primer punto: editar es una de las cosas que estaba planeando mencionar en mi respuesta, incluso con ^ X ^ E, es mucho más fácil y mucho más agradable editar un script en su editor favorito que editar en el comando. línea.
cas
2
Elegido a favor aunque el punto 4 parece un poco inútil. :)
Anthony G - justicia para Monica
@cas: con la tecla de flecha de control (o alt + f / alt + b) puedes moverte en una sola línea muy rápido. Especialmente si tiene su configuración clave de repetición automática a 50 / seg con un retardo breve como 220 ms. También puede usar control-s / control-r isearch dentro de una línea, aunque eso puede llevarlo a otro historial. Si eres bueno con control-w, alt + retroceso, alt + d y control-y, puedes hacer mucho con solo mover el cursor del teclado.
Peter Cordes
Además, algunos shells del IIRC (como ZSH, creo) tienen una combinación de teclas para invocar un editor en la línea de comando actual, lo que le permite componer un "one-liner" en su editor favorito y acceder fácilmente al historial de comandos para obtener más información. flecha y edición. (Ser capaz de modificarlo para repeticiones es lo que hace que las frases simples sean geniales, en comparación con el manejo de las opciones de línea de comandos en un script). IIRC, bash también tiene edición externa, pero no está vinculado a nada de forma predeterminada.
Peter Cordes
@PeterCordes moviendo el cursor muy rápido ni siquiera comienza a compararse con lo que puede hacer en un editor decente como vi / vim. por cierto, ^ X ^ E invoca $ EDITOR o $ VISUAL en bash (y algunos otros shells. zsh también, creo. configurable). es una buena forma de convertir el desorden ilegible que he creado en algo comprensible insertando saltos de línea y sangría; es fácil perderse en llaves anidadas o corchetes sin ellas. Por cierto, mis ventanas de terminal tienen más de 250 caracteres de ancho, por lo que mis frases pueden ser muy largas incluso sin ajustarse.
cas
7

Veo y utilizo un one-liner como herramienta de desarrollo temporal para editar repetidamente hasta que haga lo que quiero, o entiendo cómo funciona la sutileza de un subcomando.

Luego se anota (y se anota) en un archivo de texto sobre el tema que estaba investigando, o se limpia en un script que se coloca en mi bin PATH personal, posiblemente para un mayor refinamiento, parametrización, etc. Por lo general, la línea única se dividirá en más legible, incluso si no se realizan otros cambios, o si nunca uso el script nuevamente.

Configuré mi historial de shell en más de 5000 líneas, con un historial separado por terminal, y por inicio de sesión en un control remoto, etc. Odio no poder encontrar en estas historias un comando de una línea que desarrollé hace unas semanas y no pensé que lo volvería a necesitar, de ahí mi estrategia.

A veces, un grupo completo de comandos necesita hacer algo, como configurar algún hardware; al final, los copio de la historia, los limpio mínimamente y los agrego a un script de shell RUNMEcomo notas sobre lo que hice, pero también para que estén casi listos para ser utilizados nuevamente por otra persona. (Esta es la razón por la que encuentro herramientas que solo proporcionan una GUI para configurarlos.) Me parece una ganancia increíble en eficiencia, ya que a menudo algo que esperas hacer solo una vez, debe hacerse otras 5 veces. .

meuh
fuente
1
Los one-liners de +1 son excelentes para la flecha hacia arriba y editar, en lugar de implementar opciones de línea de comandos para que un script controle lo que hace por separado de lo que opera. p. ej., cambiar lnvs. ln -spor enlaces duros vs. blandos en una línea que recorre algunos archivos.
Peter Cordes
5

Me gustaría que las personas de mi equipo escriban scripts para cualquier operación que se requiera que se realice más de una vez (es decir, "flujos de trabajo" o "canalizaciones"), y para cualquier tarea única que deba documentarse (normalmente cosas como "modificar ciertas cosas en algunos conjuntos de datos para corregir datos suministrados incorrectamente" en nuestro caso). Aparte de eso, "one-liners" o scripts no importan demasiado.

No documentar adecuadamente los flujos de trabajo (como scripts o equivalentes) dificulta la introducción de nuevo personal en un proyecto, y también dificulta la transición de las personas fuera de un proyecto. Además, dificulta cualquier tipo de auditoría, y rastrear errores puede ser imposible.

Esto es independientemente del lenguaje utilizado para la programación.

Otros lugares de trabajo pueden tener costumbres o expectativas similares / diferentes, tal vez incluso escritas.

En casa, haces lo que te apetece.

Por ejemplo, escribo scripts tan pronto como hago algo no trivial en el shell, como antes de enviar una respuesta a una pregunta U&L, o cada vez que necesito probar los componentes individuales de un comando o un conjunto de comandos antes de ejecutarlo "para real".

También escribo guiones para tareas repetitivas que realmente quiero hacer de la misma manera cada vez, incluso si son simples, como

  • actualizar mi sistema OpenBSD y todos los paquetes instalados (esto se reduce a tres comandos breves y un puñado de otros para el mantenimiento, recopilados en un breve script), o
  • copia de seguridad de mi sistema en un almacenamiento fuera del sitio (esencialmente un solo comando, pero de nuevo con bastante mantenimiento y el script también me permite hacer diferencias entre instantáneas, etc., y debe funcionar sutilmente diferente en diferentes sistemas, y Lo ejecuto desde un trabajo cron), o
  • recuperar correo (de nuevo, esencialmente un solo comando, pero quiero que se comporte de manera diferente cuando se lo llame como un trabajo cron y cuando lo use desde la línea de comando, y no debería intentar recuperar correo en algunas circunstancias, y escribe un mensaje de registro corto a un archivo).

Utilizo funciones de shell (rara vez alias) por conveniencia en shells interactivos, por ejemplo, para agregar opciones a ciertos comandos de forma predeterminada ( -Fpara ls) o para crear variaciones especializadas de algunos comandos ( pmanaquí hay un mancomando que solo mira los manuales POSIX, por ejemplo) .

Kusalananda
fuente
5

Parece que tengo una opinión minoritaria sobre esto.

El término "guión" es intencionalmente confuso, deshazte de él. Este es un software que está escribiendo allí, incluso si está utilizando únicamente bashy awk.

Dentro de un equipo, prefiero escribir software con un proceso de revisión de código impuesto por una herramienta (github / gitlab / gerrit). Esto le da control de la versión como un bono. Si tiene varios sistemas, agregue herramientas de implementación continua. Y algunas suites de prueba, si los sistemas de destino son importantes. No soy religioso acerca de estos, pero sopesar los beneficios y costos.

Si tiene un equipo de administradores, lo más simple vim /root/bin/x.shes principalmente un desastre en términos de comunicación relacionada con el cambio, legibilidad del código y distribución entre sistemas. A menudo, un mal funcionamiento grave cuesta más tiempo / dinero que el proceso supuestamente "pesado".

Yo prefiero una sola línea en realidad para cualquier cosa que no merece ese proceso "pesado". Se pueden reutilizar rápidamente, con unas pocas pulsaciones de teclas, si sabe cómo utilizar su historial de shell de manera efectiva; pegado fácilmente entre sistemas no relacionados (por ejemplo, diferentes clientes).

Diferencia crucial entre (¡no revisado!) One-liners y el software revisado ("scripts"): la responsabilidad recae completamente en usted cuando ejecuta one-liners. Nunca puede decirse a sí mismo: "Acabo de encontrar esta frase en alguna parte, la ejecuté sin entender, lo que siguió no es mi culpa". Sabía que está ejecutando un software no revisado y no probado, por lo que es su culpa. Asegúrate de que sea lo suficientemente pequeño como para poder prever los resultados; maldita sea si no lo haces.

Algunas veces necesita este enfoque simplista, y establecer la barra en aproximadamente una línea de texto funciona bien en la práctica (es decir, el límite entre el código revisado y el no revisado). Algunos de mis discursos parecen terriblemente largos, pero funcionan de manera efectiva para mí. Mientras los asimile y no lo apunte a más colegas jóvenes, todo está bien. En el momento en que necesito compartirlos, paso por el proceso estándar de desarrollo de software.

kubanczyk
fuente
1
Buen punto: me gusta el término "programa" sobre "guión".
Glenn Jackman
5

Debo decir que estoy algo horrorizado por las implicaciones de las primeras partes de la pregunta. Los lenguajes de secuencias de comandos de Unix son lenguajes de programación completos, y los lenguajes de programación son lenguajes , lo que significa que son tan infinitamente maleables y flexibles como los lenguajes humanos. Y una de las características distintivas de la "maleabilidad y flexibilidad infinitas" es que casi nunca hay "una forma correcta" de expresar algo: hay muchas formas, ¡y esto es algo bueno! Entonces, sí, por supuesto , es una cuestión de preferencia personal, y no hay nada de malo en eso. Cualquiera que diga que solo hay una forma de hacer algo,

Érase una vez, la mía ~/binestaba llena de pequeños guiones "útiles" que había escrito. Pero me di cuenta de que la mayoría de ellos se acostumbraron el día que los escribí, y nunca más. Entonces, cuanto más tiempo pasa, más pequeño se binvuelve mi directorio.

No creo que haya nada malo en escribir un guión. Si imagina que usted u otra persona podrían usarlo nuevamente, por supuesto, escriba un guión. Si desea "diseñar adecuadamente" un script compatible por el bien, hágalo por todos los medios. (Incluso si nadie lo usa, escribirlo es una buena práctica).

Motivos por los que ya no escribo scripts (y, supongo, prefiero "one-liners"):

  • binel desorden del directorio tiene un costo. No pongo más cosas allí a menos que realmente pertenezcan allí.
  • Los lenguajes de secuencias de comandos que uso son idiomas que uso ; Soy muy fácil con ellos por ahora. Volver a escribir una sola línea cada vez que la necesito no parece trabajo; No siento el mandato de mantener y guardar y usar un script solo para aliviar la carga de (re) escribir. (Entre otras cosas, en algún momento la carga de volver a escribir se vuelve menor que la carga de memoria de recordar qué es un guión).
  • Otra razón por la que volver a escribir las cosas no parece una carga: el historial de comandos. Hay muchas frases que no he consignado como scripts, a pesar de que las uso todos los días, pero no tengo que volver a escribirlas; Solo los recuerdo de la historia. De esa manera son una especie de guiones o alias de pobres.
Steve Summit
fuente
4

Creo que la mayoría de las veces la solicitud de un "one-liner" es, de hecho, una solicitud de un "bocado", es decir:

En el contexto del periodismo, un fragmento de sonido se caracteriza por una frase o frase corta que captura la esencia de lo que el hablante intentaba decir, y se utiliza para resumir información ...

La gente quiere y solicita el código más corto que demuestre una solución a la pregunta formulada.

A veces, no hay forma de reducir un discurso a un "sonido", así como puede ser imposible reducir un guión a un breve "trazo". En tales casos, la única respuesta posible es un script.

Y, en general, un "sonido" nunca es un buen sustituto de todo el discurso. Por lo tanto, un "one liner" generalmente solo es bueno para transmitir una idea o para dar un ejemplo práctico de esa idea. Luego, una vez que se entiende la idea, debe expandirse (aplicarse) en un script.

Por lo tanto, escriba un "one-liner" para transmitir una idea o concepto.

Escriba su código general como scripts completos, no de una sola línea.

Isaac
fuente
1

Preguntas sobre "miedo y desdén de los guiones". Esto se debe a la situación de Q + A, donde a menudo la respuesta dice: necesita este paso y este, pero también puedo ponerlo en una línea (para que pueda copiarlo y pegarlo y usarlo como un comando largo y simple).

A veces solo puede adivinar si la Q está mejor servida con una solución de copia y pegado rápida y sucia, o si un script "agradable" es exactamente lo que la Q necesita entender.

Muchos de los pros y los contras aquí son ciertos, pero aún así están perdiendo el punto. Incluso diría que las definiciones en la Q no son útiles.

Los archivos de historial de shell son "one liners", pero aún así se guardan. La función bash operate-and-get-next (C-o)incluso te permite repetirlos semiautomáticamente.

Apenas veo la palabra función mencionada. Esa es la forma de almacenar "scripts" y "one liners". Y con unas pocas teclas puede cambiar entre ambos formatos. Un comando compuesto a menudo puede adaptarse a ambos.

history -r filees la forma de leer una o varias líneas de comando (y comentarios) de cualquier archivo, no solo del archivo de historial predeterminado. Solo se necesita una organización mínima. No debe confiar en un gran tamaño de archivo de historial y búsqueda de historial para recuperar (alguna) versión de una línea de comando.

Las funciones deben definirse de manera similar. Para empezar, coloque thisfunc() {...}un archivo "funciones" en su directorio de inicio y búsquelo. Sí, esto es una sobrecarga, pero es la solución general.

Si almacena cada línea de comando y cada variación del script en un archivo separado, es un desperdicio (teórico, pero objetivo) de bloques del sistema de archivos.

Un trazador de líneas no tiene nada rápido y sucio por sí mismo. Es más la parte de copiar y pegar que está mal vista, y cómo solo confiamos en el historial de comandos para guardarlo para nosotros.


@sitaram: su única línea realmente tiene características poco exclusivas : línea shebang, nueva línea, cuatro llamadas de servicios públicos, dos de ellas son "programas". Creo que el script perl original se veía mejor y se ejecutó más rápido y no desperdició ningún byte extendiéndose en 60 líneas. Y perl también te permite apretar un poco.

Para mí, ese es exactamente el tipo de revestimiento que se debe evitar. ¿Le gustaría tener eso (pegado) en su línea de comando? No, y luego, si lo almacena en un archivo de todos modos (no importa el script o la función), podría invertir dos o tres bytes de nueva línea para que se vea --- agradable.


10 minutos. más tarde: solo quería comprobar si puedo decir "dos programas sed" o si son "dos sustituciones / llamadas sed". Pero la respuesta de sitaram se ha ido. Mi respuesta todavía tiene sentido, espero.

archivo de rasta
fuente