Estoy buscando una forma de reemplazar las cadenas de marcador de posición en un archivo de plantilla con valores concretos, con herramientas comunes de Unix (bash, sed, awk, quizás perl). Es importante que el reemplazo se realice en una sola pasada, es decir, lo que ya está escaneado / reemplazado no debe considerarse para otro reemplazo. Por ejemplo, estos dos intentos fallan:
echo "AB" | awk '{gsub("A","B");gsub("B","A");print}'
>> AA
echo "AB" | sed 's/A/B/g;s/B/A/g'
>> AA
El resultado correcto en este caso es, por supuesto, BA.
En general, la solución debería ser equivalente a escanear la entrada de izquierda a derecha para una coincidencia más larga con una de las cadenas de reemplazo dadas, y para cada coincidencia, realizar un reemplazo y continuar desde ese punto en adelante en la entrada (ninguna de las la entrada ya leída ni los reemplazos realizados deben considerarse para las coincidencias). En realidad, los detalles no importan, solo que los resultados del reemplazo nunca se consideran para otro reemplazo, en su totalidad o en parte.
NOTA Solo estoy buscando soluciones genéricas correctas. No proponga soluciones que fallen para ciertas entradas (archivos de entrada, búsqueda y reemplazo de pares), por improbables que puedan parecer.
tr AB BA
.Respuestas:
OK, una solución general. La siguiente función bash requiere
2k
argumentos; cada par consta de un marcador de posición y un reemplazo. Depende de usted citar las cadenas apropiadamente para pasarlas a la función. Si el número de argumentos es impar, se agregará un argumento vacío implícito, que eliminará efectivamente las ocurrencias del último marcador de posición.Ni los marcadores de posición ni los reemplazos pueden contener caracteres NUL, pero puede usar
\
paisajes C estándar , como\0
si necesitaNUL
s (y, en consecuencia, debe escribir\\
si desea a\
).Requiere las herramientas de compilación estándar que deben estar presentes en un sistema tipo posix (lex y cc).
Suponemos que
\
ya se ha escapado si es necesario en los argumentos, pero debemos escapar de las comillas dobles, si están presentes. Eso es lo que hace el segundo argumento al segundo printf. Como lalex
acción predeterminada esECHO
, no debemos preocuparnos por eso.Ejemplo de ejecución (con tiempos para los escépticos; es solo una computadora portátil barata):
Para entradas más grandes, podría ser útil proporcionar un indicador de optimización
cc
y, para la compatibilidad actual de Posix, sería mejor usarloc99
. Una implementación aún más ambiciosa podría intentar almacenar en caché los ejecutables generados en lugar de generarlos cada vez, pero no son exactamente costosos de generar.Editar
Si tiene tcc , puede evitar la molestia de crear un directorio temporal y disfrutar del tiempo de compilación más rápido que ayudará en las entradas de tamaño normal:
fuente
fn() { tcc ; } <<CODE\n$(gen code)\nCODE\n
. Sin embargo, ¿puedo preguntar, esta es una respuesta increíble y la voté tan pronto como la leí, pero no entiendo lo que está sucediendo con la matriz de shell? ¿Qué hace"${@//\"/\\\"}"
esto?Algo como esto siempre reemplazará cada aparición de sus cadenas de destino solo una vez, ya que ocurren en
sed
las secuencias en una mordida por línea. Esta es la forma más rápida que puedo imaginar que lo harías. Por otra parte, yo no escribo C. Pero esto lo hace ocupe de forma fiable delimitadores nulos si lo desea. Vea esta respuesta para ver cómo funciona. Esto no tiene problemas con ningún carácter de shell especial contenido o similar, pero es específico de la ubicación ASCII o, en otras palabras,od
no generará caracteres de varios bytes en la misma línea y solo hará uno por cada. Si esto es un problema, querrás agregarloiconv
.fuente
sed
y guardar hasta un valor nulo o algo y luegosed
escribir el guión de este; o ponerlo en una función de shell y darle valores a un mordisco por línea como"/$1/"
..."/$2/"
- tal vez yo también escriba esas funciones ...PLACE1
,PLACE2
yPLA
.PLA
siempre gana. OP dice: "equivalente a escanear la entrada de izquierda a derecha para una coincidencia más larga con una de las cadenas de reemplazo dadas" (énfasis agregado)Una
perl
solución Incluso si algunos declararon que no es posible, encontré uno, pero en general una simple coincidencia y reemplazo no es posible e incluso empeora debido al retroceso de un NFA, el resultado puede ser inesperado.En general, y esto debe decirse, el problema arroja resultados diferentes que dependen del orden y la longitud de las tuplas de reemplazo. es decir:
y la entrada
AAA
da como resultadoBBB
oCCB
.Aquí el código:
Checkerbunny:
fuente