2012-02-03 17 views
6

Estoy tratando de hacer mi tarea que está restringida a solo usar sed para filtrar un archivo de entrada a un formato de salida determinado. Aquí está el archivo de entrada (llamado stocks):Cómo escribo un script sed para grep información de un archivo de texto

Symbol;Name;Volume 
================================================ 

BAC;Bank of America Corporation Com;238,059,612 
CSCO;Cisco Systems, Inc.;28,159,455 
INTC;Intel Corporation;22,501,784 
MSFT;Microsoft Corporation;23,363,118 
VZ;Verizon Communications Inc. Com;5,744,385 
KO;Coca-Cola Company (The) Common;3,752,569 
MMM;3M Company Common Stock;1,660,453 

================================================ 

Y la salida tiene que ser:

BAC, CSCO, INTC, MSFT, VZ, KO, MMM 

Yo he venido para arriba con una solución, pero no es eficiente. Aquí está mi sed guión (llamado try.sed):

/.*;.*;[0-9].*/ { N 
N 
N 
N 
N 
N 
s/\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*/\1, \2, \3, \4, \5, \6, \7/gp 
} 

El comando que corro en el caparazón es:

$ sed -nf try.sed stocks 

Mi pregunta es, ¿Hay una mejor manera de usar sed para obtener el mismo resultado ? El script que escribí solo funciona con 7 líneas de datos. Si los datos son más largos, necesito volver a modificar mi secuencia de comandos. No estoy seguro de cómo puedo hacerlo mejor, ¡así que estoy aquí pidiendo ayuda!

Gracias por cualquier recomendación.

+5

+1 para la admisión de esto es la tarea y por esa salvaje 's/\ (. * \); ....../'cosa que pones ahí! Buena suerte. – shellter

Respuesta

2

una forma más usando sed:

sed -ne '/^====/,/^====/ { /;/ { s/;.*$// ; H } }; $ { g ; s/\n// ; s/\n/, /g ; p }' stocks 

Salida:

BAC, CSCO, INTC, MSFT, VZ, KO, MMM 

Explicación:

-ne    # Process each input line without printing and execute next commands... 
/^====/,/^====/ # For all lines between these... 
{ 
    /;/    # If line has a semicolon... 
    { 
    s/;.*$//  # Remove characters from first semicolon until end of line. 
    H    # Append content to 'hold space'. 
    } 
}; 
$     # In last input line... 
{ 
    g    # Copy content of 'hold space' to 'pattern space' to work with it. 
    s/\n//   # Remove first newline character. 
    s/\n/, /g  # substitute the rest with output separator, comma in this case. 
    p    # Print to output. 
+0

wow, gracias Birei! No sabía que podía hacer doble {} y olvidé que puedo usar el comando sustituir w/o g para unir la primera coincidencia que se produce. Todavía tengo algunas preguntas aquí. 1. ¿Por qué el último bloque está en el último patrón de línea ($)? 2. Para la 2da sustitución de una nueva línea. ¿Es su propósito eliminar la línea vacía? 2. Para la última sustitución de una nueva línea, ¿cómo es que no reemplazó la línea nueva después de "MMM"? Me diste una gran explicación pero sigo sin entender el propósito de $ {}. Espero que puedas ayudarme a entenderlo más. ¡¡Muchas gracias por tu ayuda!! – Jaycee

+0

@Jecee: [1] Guardo las cadenas deseadas en 'espacio de espera' durante el proceso del archivo y solo en la última línea recupero ese contenido, lo modifico e imprimo. [2] El comando 'H' agrega' \ n' más el contenido de 'patrón de espacio' a 'espacio', por lo que en la última línea el contenido será como '\ nBAC \ nCSCO \ nINTC \ nMSFT \ nVZ \ nKO \ nMMM '. Luego elimino primero '\ n' y sustituyo el resto con', ' – Birei

+0

Ahhh ..... ¡¡¡Lo tengo ahora !! ¡¡¡¡¡Muchas gracias!!!!!Es genial usar H y g ... =) No estoy seguro de por qué mi maestro no nos enseñó estos comandos. ¡¡¡¡¡Gracias de nuevo!!!!!^O ^ – Jaycee

0

Este comando sed debe producir su salida requerida:

sed -rn '/[0-9]+$/{s/^([^;]*).*$/\1/p;}' file.txt 

O en Mac:

sed -En '/[0-9]+$/{s/^([^;]*).*$/\1/p;}' file.txt 
+4

Es tarea. Realmente no deberías solo darle una respuesta. –

+0

Querido anubhava, he ejecutado tu comando pero el resultado no está en una línea. Uno de los desafíos es reemplazar todas las líneas nuevas por comas y 1 espacio, excepto la última línea. No debería haber una coma después de la última. – Jaycee

+0

Sí, mi script se comporta exactamente como grep -o, ya que ahora me doy cuenta de que es una tarea, te dejaré el resto del guión. – anubhava

2

Editar: He editado mi algoritmo, ya que me había olvidado de considerar la cabecera y pie de página (pensé que eran solo para nuestro beneficio).

sed, por su diseño, accede a cada línea de un archivo de entrada, y luego realiza expresiones en las que coinciden con alguna especificación (o ninguna). Si está adaptando su secuencia de comandos a un cierto número de líneas, ¡definitivamente está haciendo algo mal! No te escribiré un guión porque es tarea, pero la idea general de una forma de hacerlo es escribir un guión que haga lo siguiente. Piense en el orden como el orden en que las cosas deberían estar en un guión.

  1. Omita las tres primeras líneas con d, lo que elimina el espacio del patrón y pasa inmediatamente a la siguiente línea.
  2. Para cada línea que no sea una línea en blanco, realice los siguientes pasos. (Todo esto estaría en un solo conjunto de llaves).
    1. Reemplace todo después de incluir el primer punto y coma (;) con una coma y espacio (",") usando el comando s (sustituto).
    2. Agregue el espacio del patrón actual en hold buffer (consulte H).
    3. Eliminar el espacio de patrones y pasar a la siguiente línea, al igual que en el paso 1.
  3. Para cada línea que llega a este punto de la secuencia de comandos (debe ser la primera línea en blanco), recuperar el contenido de el espacio de espera en el espacio del patrón. (Esto sería después de las llaves.)
  4. Sustitutos todos saltos de línea en el espacio del patrón con nada.
  5. A continuación, sustituya la última coma y espacio en el espacio del patrón sin nada.
  6. Finalmente, salga del programa para no procesar más líneas. Mi script funcionó sin esto, pero no estoy 100% seguro de por qué.

Habiendo dicho eso, esa es solo una forma de hacerlo. sed a menudo ofrece diferentes formas de complejidad variable para realizar una tarea. Una solución que escribí con este método tiene 10 líneas de longitud.

Como nota, no me molesto en suprimir la impresión (con -n) o en la impresión manual (con p); cada línea está impresa por defecto. Mi escritura funciona así:

$ sed -f companies.sed companies 
BAC, CSCO, INTC, MSFT, VZ, KO, MMM 
+0

@Jaycee ¿Con qué parte de lo anterior tienes problemas? ¡Me gustaría mejorar mi explicación si puedo! –

+0

hola, Dan, gracias por la pista.Para el 1er paso, obtendría todos los símbolos con una coma y un espacio. Pero estoy teniendo problemas para hacer el 2do paso. ¿Cómo obtengo cada línea que no es la última línea? Técnicamente, MMM no es la última línea. ============ es la última línea. Estoy tan confundido y realmente no sé cómo proceder. ¿Podrías por favor elaborar un poco más? ¡Muchas gracias por tu ayuda! – Jaycee

+0

Puedo obtener el último de la siguiente manera: /[0-9] $/{N N s/\ (. * \);. *;. * \ N \ n \ = */\ 1/gp } – Jaycee

0

Esto podría funcionar para usted:

sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;q};d' stocks 
  • No queremos que las partidas así que vamos a eliminarlos. 1d
  • Todos los elementos de datos están delimitados por ;, así que concentrémonos en esas líneas. /;/
  • De las cosas de arriba eliminar todo desde el primer ; hasta el final de la línea y luego rellenarlo de distancia en el espacio de la bodega (SA) {s/;.*//;H}
  • Al llegar a la última línea, sobrescribirlo con el SA utilizando el comando g, elimine la primera línea nueva (generada por el comando H), reemplace todas las líneas nuevas subsiguientes con una coma y un espacio e imprima lo que quede. ${g;s/.//;s/\n/, /g;q}
  • Eliminar todo lo demás d

He aquí una sesión de terminal que muestra el refinamiento gradual de la construcción de un comando sed:

cat <<! >stock # paste the file into a here doc and pass it on to a file 
> Symbol;Name;Volume 
> ================================================ 
> 
> BAC;Bank of America Corporation Com;238,059,612 
> CSCO;Cisco Systems, Inc.;28,159,455 
> INTC;Intel Corporation;22,501,784 
> MSFT;Microsoft Corporation;23,363,118 
> VZ;Verizon Communications Inc. Com;5,744,385 
> KO;Coca-Cola Company (The) Common;3,752,569 
> MMM;3M Company Common Stock;1,660,453 
> 
> ================================================ 
> ! 
sed '1d;/;/!d' stock # delete headings and everything but data lines 
BAC;Bank of America Corporation Com;238,059,612 
CSCO;Cisco Systems, Inc.;28,159,455 
INTC;Intel Corporation;22,501,784 
MSFT;Microsoft Corporation;23,363,118 
VZ;Verizon Communications Inc. Com;5,744,385 
KO;Coca-Cola Company (The) Common;3,752,569 
MMM;3M Company Common Stock;1,660,453 
sed '1d;/;/{s/;.*//p};d' stock # delete all non essential data 
BAC 
CSCO 
INTC 
MSFT 
VZ 
KO 
MMM 
sed '1d;/;/{s/;.*//;H};${g;l};d' stock # use the l command to see what's really there! 
\nBAC\nCSCO\nINTC\nMSFT\nVZ\nKO\nMMM$ 
sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;l};d' stock # refine refine 
BAC, CSCO, INTC, MSFT, VZ, KO, MMM$ 
sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;q};d' stock # all done! 
BAC, CSCO, INTC, MSFT, VZ, KO, MMM 
Cuestiones relacionadas