Ésta es una cuestión totalmente no trivial.
Esto funciona reemplazando el primer espacio dentro de las citas con subrayado:
$ sed 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' f.txt
"a_aa" MM "bbb_ b"
MM MM
MM"b_b "
$
Para este ejemplo, donde no hay más de dos espacios dentro de cualquiera de las cotizaciones, es tentador simplemente repetir el comando, pero que da un resultado incorrecto:
$ sed -e 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' \
> -e 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' f.txt
"a_aa"_ MM "bbb_ b"
MM MM
MM"b_b_"
$
Si su versión de sed
apoya '' expresiones regulares extendidas, entonces esto funciona para los datos de ejemplo:
$ sed -E \
> -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
> -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
> -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
> f.txt
"a_aa" MM "bbb__b"
MM MM
MM"b_b_"
$
Tiene que repetir esa horrible expresión regular para cada espacio entre comillas dobles, de ahí tres veces para la primera línea de datos.
la expresión regular puede ser explicado como:
- Comenzando por el principio de una línea,
- Buscar las secuencias de 'cero o más no-citas, opcionalmente seguido de una cita, no hay espacios o las cotizaciones , y una cita ', toda la asamblea repite cero o más veces,
- Seguido de una cita, cero o más citas, espacios, un espacio y cero o más citas, y una cita.
- Reemplace el material coincidente con la parte delantera, el material al comienzo del pasaje citado actual, un guión bajo y el material posterior del pasaje citado actual.
Debido al inicio de anclaje, esto tiene que ser repetida una vez al blanco ... pero sed
tiene una estructura iterativa, por lo que se puede hacer con:
$ sed -E -e ':redo
> s/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/
> t redo' f.txt
"a_aa" MM "bbb__b"
MM MM
MM"b_b_"
$
El :redo
define una etiqueta; el comando s///
es como antes; El comando t redo
salta a la etiqueta si hubo alguna sustitución desde la última lectura de una línea o salta a una etiqueta.
Dada la discusión en los comentarios, hay un par de puntos vale la pena mencionar:
La opción se aplica a -E
sed
en MacOS X (10.7.2 probado).La opción correspondiente para la versión GNU de sed
es -r
(o --regex-extended
). La opción -E
es consistente con grep -E
(que también usa expresiones regulares extendidas). Los 'sistemas Unix clásicos' no son compatibles con ERE con sed
(Solaris 10, AIX 6, HP-UX 11).
Usted puede substituir la ?
utilicé (que es el único personaje que obliga al uso de un ERE en lugar de un BRE) con *
, y luego lidiar con los paréntesis (que requieren barras invertidas delante de ellos en una BRE para convertirlos en paréntesis de captura), dejando el guión:
sed -e ':redo
s/^\(\([^"]*\("[^ "]*"\)*\)*\)\("[^ "]*\) \([^"]*"\)/\1\4_\5/g
t redo' f.txt
Esto produce el mismo resultado en la misma entrada - probé algunos patrones ligeramente más complejos en la entrada:
"a aa" MM "bbb b"
MM MM
MM"b b "
"c c""d d""e e" X " f "" g "
"C C" "D D" "E E" x " F " " G "
Thi s da la salida:
"a_aa" MM "bbb__b"
MM MM
MM"b_b_"
"c_c""d_d""e__e" X "_f_""_g_"
"C_C" "D_D" "E__E" x "_F_" "_G_"
Incluso con la notación BRE, sed
apoyaron la \{0,1\}
notación para especificar 0 o 1 apariciones del término RE anterior, por lo que la versión ?
podría traducirse a un BRE usando:
sed -e ':redo
s/^\(\([^"]*\("[^ "]*"\)\{0,1\}\)*\)\("[^ "]*\) \([^"]*"\)/\1\4_\5/g
t redo' f.txt
Esto produce la misma salida que las otras alternativas.
BTW: Buena pregunta, especialmente con la buena entrada de ejemplo y la salida requerida. –