2009-04-27 25 views
5

Me estoy perdiendo totalmente en la programación de shell, principalmente porque cada sitio que uso ofrece una herramienta diferente para hacer coincidencia de patrones. Entonces mi pregunta es qué herramienta usar para hacer una coincidencia simple de patrones en transmisión por tubería.haciendo coincidir texto entre comillas (novato)

context: He named.conf file, y necesito todos los nombres de zonas en un archivo simple para su posterior procesamiento. Entonces lo hago ~ $ cat named.local | grep zone y perderse por completo aquí. Mi salida es de aproximadamente cien líneas nuevas en la forma 'zone' domain.tld '{' y necesito texto entre comillas dobles.

Gracias por mostrarnos una forma de hacerlo.

J

Respuesta

23

creo que lo que estás buscando es sed ... es un s TREAM ed itor, que le permita realizar reemplazos en una base de línea por línea.

Como lo está explicando, el comando `cat named.local | zona grep' le da una salida un poco como esto:

zone "domain1.tld" { 
zone "domain2.tld" { 
zone "domain3.tld" { 
zone "domain4.tld" { 

supongo que desea que la salida sea algo como esto, ya que dijo que necesita el texto entre comillas dobles:

"domain1.tld" 
"domain2.tld" 
"domain3.tld" 
"domain4.tld" 

Entonces, en realidad, desde cada línea solo queremos el texto entre las comillas dobles (incluidas las comillas dobles)

No estoy seguro de que esté familiarizado con Regular Expressions, pero son una herramienta invaluable para cualquier persona que escriba scripts de shell. Por ejemplo, la expresión regular /.o.e/ coincidiría con cualquier línea donde haya una palabra con la segunda letra en minúsculas o, y la 4ª era e. Esto coincidiría con palabras cadena que contiene como "zone", "tone", o incluso "I am tone-deaf."

El truco que había que utilizar el carácter . (punto) en el sentido de "cualquier letra". Hay un par de otros caracteres especiales, como * que significa "repetir el carácter anterior 0 o más veces". Por lo tanto una expresión regular como a* coincidiría "a", "aaaaaaa", o una cadena vacía: ""

para que pueda coincidir la cadena dentro de las comillas usando: /".*"/

Hay otra cosa que se sabe acerca sed (y por los comentarios, ¡ya lo hace!) - permite retroceder. Una vez que le haya dicho cómo reconocer una palabra, puede hacer que use esa palabra como parte del reemplazo. Por ejemplo, digamos que usted quiere convertir esta lista:

Billy "The Kid" Smith 
Jimmy "The Fish" Stuart 
Chuck "The Man" Norris 

En esta lista:

The Kid 
The Fish 
The Man 

En primer lugar, se buscaría la cadena dentro de las comillas. Ya vimos que era /".*"/.

A continuación, queremos utilizar lo que hay dentro de las comillas.Podemos grupo que utilizando parens: /"(.*)"/

Si quisiéramos reemplazar el texto con las comillas con un carácter de subrayado, nos gustaría hacer un reemplazo: s/"(.*)"/_/, y que nos dejaría con:

Billy _ Smith 
Jimmy _ Stuart 
Chuck _ Norris 

¡Pero hemos retrocedido! Eso nos permitirá recordar lo que estaba dentro de los parens, usando el símbolo \1. Así que si hacemos ahora: s/"(.*)"/\1/ nos pondremos:

Billy The Kid Smith 
Jimmy The Fish Stuart 
Chuck The Man Norris 

Debido a que las cotizaciones no estaban en los parens, que no formaban parte de los contenidos de \1!

Para dejar solo el material dentro de las comillas dobles, debemos hacer coincidir toda la línea. (. Que significa "fin de línea") para hacer eso tenemos ^ (que significa "principio de la línea"), y $

Así que ahora si utilizamos s/^.*"(.*)".*$/\1/, conseguiremos:

The Kid 
The Fish 
The Man 

¿Por qué? Vamos a leer la expresión regular s/^.*"(.*)".*$/\1/ de izquierda a derecha:

  • s/ - Iniciar una sustitución expresión regular
  • ^ - Busque el principio de la línea. Comienza desde allí.
  • .* - Sigue, leer todos los personajes, hasta que ...
  • " - ... hasta llegar a una doble cita.
  • ( - comience un grupo de caracteres que tal vez deseemos recordar más adelante al retroceder.
  • .* - Sigue, leer todos los personajes, hasta que ...
  • ) - (! Pssst cerrar el grupo)
  • " - ... hasta llegar a una doble cita.
  • .* - Sigue, leer todos los personajes, hasta que ...
  • $ - El final de la línea!

  • / - utilizar lo que hay después de esto para reemplazar lo que emparejado

  • \1 - pegar el contenido del primer grupo (lo que había en los parens) emparejado.
  • / - fin de la expresión regular

En la llanura Inglés:. "Leer toda la línea, la copia a un lado el texto entre comillas dobles A continuación, reemplace la línea entera con el contenido entre las dobles qoutes."

Incluso se puede añadir de comillas dobles alrededor del texto reemplazando s/^.*"(.*)".*$/"\1"/, por lo que obtendrá:

"The Kid" 
"The Fish" 
"The Man" 

y que puede ser utilizado por sed para reemplazar la línea con el contenido desde dentro de las comillas:

sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/" 

(esto es sólo concha escapó para hacer frente a las comillas dobles y las barras y esas cosas.)

de modo que todo el comando wo ULD ser algo como:

cat named.local | grep zone | sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/" 
+0

Sí, lo estoy usando ahora, pero creo que debería haber una manera más fácil de hacerlo, porque ahora uso sed-e 's/zone "// g' | sed-e 's /" { // g 'para eliminar el comienzo y el final de un archivo en lugar de simplemente coincidir con el medio. – jpou

+1

Afeitar el principio y el final es perfectamente aceptable. Esto no es competencia, si funciona, está bien. Si desea hacerlo haciendo coincidir el texto entre comillas, eche un vistazo a "captura de grupos". – zoul

+0

ugh. Pasé demasiado tiempo tipeando eso, y todavía no está hecho ... parece que todos nos ganaron. Pero me alegra que ya lo hayas descifrado :-) – scraimer

1

1.

[email protected]:etc$ cat named.conf | grep zone 
zone "." IN { 
zone "localhost" IN { 
    file "localhost.zone"; 
zone "0.0.127.in-addr.arpa" IN { 

2.

[email protected]:etc$ cat named.conf | grep ^zone 
zone "." IN { 
zone "localhost" IN { 
zone "0.0.127.in-addr.arpa" IN { 

3.

[email protected]:etc$ cat named.conf | grep ^zone | sed 's/.*"\([^"]*\)".*/\1/' 
. 
localhost 
0.0.127.in-addr.arpa 

La expresión regular es .*"\([^"]*\)".*, que coincide con:

  1. cualquier número de caracteres: .*
    • una cotización: "
    • comienza a tener en cuenta para más adelante: \(
    • cualquier carácter excepto cita: [^"]*
    • extremos grupo de recordar: Cotización \)
    • de cierre : "
    • y cualquier número de caracteres: .*

Al llamar sed, la sintaxis es 's/what_to_match/what_to_replace_it_with/'. Las comillas simples están ahí para evitar que su expresión regular se expanda en bash. Cuando "recuerdas" algo en la expresión regular mediante el uso de parens, puedes recuperarlo como \1, \2, etc. Fiddle con él por un tiempo.

2

Bueno, nadie cut mencionado todavía, así que, para demostrar que hay muchas maneras de hacer algo con la cáscara:

% grep '^zone' /etc/bind/named.conf | cut -d' ' -f2 
"gennic.net" 
"generic-nic.net" 
"dyn.generic-nic.net" 
"langtag.net" 
0

Mientras alguien está señalando sed/awk, voy a señalar que grep es redundante.

sed -ne '/^zone/{s/.*"\([^"]*\)".*/\1/;p}' /etc/bind/named.conf 

Esto le ofrece lo que está buscando sin las comillas (mueva las comillas dentro del paréntesis para mantenerlas).En awk, es aún más sencillo con las comillas:

awk '/^zone/{print $2}' /etc/bind/named.conf 

que intenta evitar las tuberías tanto como sea posible (pero no más). Recuerde, Don't pipe cat. No es necesario. Y, como awk y sed duplican el trabajo de grep, tampoco pipe grep. Al menos, no en sed o awk.

Personalmente, probablemente hubiera usado perl. Pero eso es porque probablemente habría hecho el resto de lo que sea que esté haciendo en perl, convirtiéndolo en un detalle menor (y poder sorber todo el archivo y expresar todo al mismo tiempo, ignorar \ n sería una ventaja para los casos en que No controlo/etc/bind, como en un servidor web compartido). Pero, si tuviera que hacerlo en shell, uno de los dos anteriores sería la forma en que lo abordaría.

Cuestiones relacionadas