Supongamos que hay texto de un archivo:
(bookmarks
("Chapter 1 Introduction 1" "#1"
("1.1 Problem Statement and Basic Definitions 23" "#2")
("Exercises 31" "#30")
("Notes and References 42" "#34"))
)
Quiero agregar 11 a cada número seguido de un "
en cada línea si hay uno, es decir
(bookmarks
("Chapter 1 Introduction 12" "#12"
("1.1 Problem Statement and Basic Definitions 34" "#13")
("Exercises 42" "#41")
("Notes and References 53" "#45"))
)
Aquí está mi solución usando GNU AWK y regex:
awk -F'#' 'NF>1{gsub(/"(\d+)\""/, "\1+11\"")}'
es decir, quiero reemplazar (\d+)\"
con \1+10\"
, donde \1
representa el grupo (\d+)
. Pero no funciona. ¿Cómo puedo hacer que funcione?
Si gawk no es la mejor solución, ¿qué más se puede usar?
Respuestas:
Prueba esto (se necesita gawk).
Prueba con tu ejemplo:
Tenga en cuenta que este comando no funcionará si los dos números (por ejemplo, 1 "y" # 1 ") son diferentes, o si hay más números en la misma línea con este patrón (por ejemplo, 23" ... 32 "..." # 123 ") en una línea.
ACTUALIZAR
Como @Tim (OP) dijo que el número seguido de la
"
misma línea podría ser diferente, hice algunos cambios en mi solución anterior y lo hice funcionar para su nuevo ejemplo.Por cierto, del ejemplo siento que podría ser una tabla de estructura de contenido, por lo que no veo cómo los dos números podrían ser diferentes. Primero sería el número de página impresa, y segundo con # sería el índice de la página. Estoy en lo cierto?
De todos modos, conoces mejor tus requisitos. Ahora la nueva solución, todavía con gawk (divido el comando en líneas para que sea más fácil de leer):
prueba con tu nuevo ejemplo:
EDIT2 basado en el comentario de @Tim
Tiene razón para el separador tanto en la parte de entrada como en la de salida. Definió separador como:
Hay dos comillas dobles, porque es más fácil capturar los dos números que desea (según su entrada de ejemplo).
¡Exactamente!
Esto es de http://www.gnu.org/s/gawk/manual/html_node/String-Functions.html . puede leer para obtener un uso detallado de gensub.
fuente
awk -F'#'
, parece que solo quiere hacer el cambio en la parte después del '#'?FS=OFS="\" \"#"
significa que el separador de campo tanto en la entrada como en la salida es comillas dobles, espacios, comillas dobles y #? ¿Por qué especificar comillas dobles dos veces? (2) en/.* ([0-9]+)$/
, ¿$
significa el final de la cadena? (3) en el tercer argumento de gensub (), ¿cuál es la diferencia entre"g"
y"G"
?A diferencia de casi todas las herramientas que proporcionan sustituciones regexp, awk no permite referencias posteriores como
\1
en el texto de reemplazo. GNU Awk da acceso a grupos coincidentes si usa lamatch
función , pero no con~
osub
ogsub
.Tenga en cuenta también que incluso si
\1
fuera compatible, su fragmento agregaría la cadena+11
, no realizaría un cálculo numérico. Además, tu expresión regular no es del todo correcta, estás combinando cosas como"42""
y no"#42"
.Aquí hay una solución awk (advertencia, no probada). Solo realiza un solo reemplazo por línea.
Sería más simple en Perl.
fuente
awk
puede hacerlo, pero no es directo, incluso utilizando referencias inversas.GNU awk tiene referencias parciales (parciales), en forma de gensub .
Las instancias de
123"
se envuelven temporalmente\x01
y se\x02
marcan como no modificadas (forsub()
. CoO simplemente puede pasar por el ciclo cambiando candidatos a medida que avanza, en cuyo caso, no se necesitan referencias y "paréntesis"; pero se necesita hacer un seguimiento del índice de caracteres.
Aquí hay otra forma, usando
gensub
y arraysplit
y\x01
como delimitador de campo (para división ) ... \ x02 marca un elemento de matriz como candidato para la suma aritmética.fuente
"\x01\\1\"\x02"
significa? Todavía no entiendo\x01
y\x02
. (2) ¿cuán diferente es el retorno$0
porgensub
y el$0
como último argumentogensub
?\x01
y\x02
se utilizan como marcadores de sustitución. Estos valores son altamente improbable que sea, en cualquier normales archivo de texto, por lo que son igualmente "muy" seguro de usar (es decir. No encontrará un enfrentamiento con los ya existentes) .. Son simplemente etiquetas temporales .. Re$0=gensub(... $0)
.. ver esto enlace Funciones de manipulación de cadenas , pero en resumen: (gensub) devuelve la cadena modificada como resultado de la función y la cadena de destino original no cambia. ... El$0=
simplemente modifica el objetivo original ..Dado que las soluciones en (g) awk parecen volverse bastante complejas, quería agregar una solución alternativa en Perl:
Explicación:
-w
habilita advertencias (que le advertirán de posibles efectos no deseados).-p
implica un lazo alrededor del código que funciona de forma similar a la sed o awk, ahorrando cada línea de entrada de forma automática en la variable por defecto,$_
.-e
le dice a Perl que el código del programa sigue en la línea de comando, no en un archivo de script.s/.../.../
) en$_
, donde una secuencia de dígitos, si es seguida por una"
, será reemplazada por la secuencia, interpretada como un número en la suma, más 11.(?=pattern)
busca el"
sin tomarlo en el partido, por lo que no tenemos que repetirlo en el reemplazo. La variable MATCH$&
en el reemplazo contendrá solo el número./e
modificador de la expresión regular le diceperl
a "ejecutar" el reemplazo como código en lugar de tomarlo como una cadena./g
modificador hace el reemplazo "global", repitiéndolo en cada partido en la línea.$&
Desafortunadamente, la variable MATCH será perjudicial para el rendimiento del código en las versiones de Perl anteriores a 5.20. Una solución más rápida (y no mucho más compleja) usaría la agrupación y la referencia inversa$1
en su lugar:Y si la afirmación de anticipación parece demasiado confusa, también podría reemplazar la comilla explícitamente:
fuente