2012-02-03 7 views
6

Sto provando a fare i compiti che è limitato a utilizzare solo sed per filtrare un file di input in un determinato formato di output. Ecco il file di input (chiamato stocks):Come scrivere uno script sed per grep informazioni da un file di testo

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 

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

E l'uscita deve essere:

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

sono venuto su con una soluzione, ma non è efficiente. Qui è il mio sed copione (chiamato 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 
} 

Il comando che ho eseguito sulla shell è:

$ sed -nf try.sed stocks 

La mia domanda è: esiste un modo migliore di usare sed per ottenere lo stesso risultato ? Lo script che ho scritto funziona solo con 7 righe di dati. Se i dati sono più lunghi, ho bisogno di ri-modificare il mio script. Non sono sicuro di come riuscirò a migliorare, quindi sono qui per chiedere aiuto!

Grazie per eventuali consigli.

+5

+1 per l'ammissione di questo compito e per questo selvaggio 's/\ (. * \); ....../'cosa ci hai messo dentro! In bocca al lupo. – shellter

risposta

2

Un altro modo con sed:

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

uscita:

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

Spiegazione:

-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, grazie Birei! Non sapevo di poterlo raddoppiare {} e ho dimenticato che posso semplicemente usare il comando sostitutivo w/o g per abbinare la 1a corrispondenza verificatasi. Ho ancora qualche domanda qui. 1. Perché l'ultimo blocco si trova sull'ultimo modello di linea ($)? 2. Per la seconda sostituzione di una nuova linea. Il suo scopo è rimuovere la linea vuota? 2. Per l'ultima sostituzione di una nuova linea, come mai non è stata sostituita la nuova riga dopo "MMM"? Mi hai fornito un'ottima spiegazione, ma ancora non capisco lo scopo di $ {}. Spero che tu possa aiutarmi a capirlo di più. Grazie mille per il vostro aiuto!! – Jaycee

+0

@Jaycee: [1] Salva le stringhe desiderate nello 'spazio di attesa' durante il processo del file e solo nell'ultima riga recupera il contenuto, lo modifica e lo stampo. [2] Il comando 'H' aggiunge' \ n' più il contenuto di 'spazio pattern' a 'hold space', quindi alla fine il contenuto sarà come '\ nBAC \ nCSCO \ nINTC \ nMSFT \ nVZ \ nKO \ nMMM '. Quindi rimuovo prima '\ n' e sostituisco il resto con', ' – Birei

+0

Ahhh ..... L'ho capito ora !!!! Grazie mille!!!!!È bello usare H e g .... =) Non sono sicuro del motivo per cui il mio insegnante non ci ha insegnato questi comandi. Grazie ancora!!!!!^O ^ – Jaycee

0

Questo comando sed dovrebbe produrre l'output richiesto:

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

O su Mac:

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

È compito. Davvero non dovresti dargli una risposta. –

+0

Gentile anubhava, ho eseguito il comando ma l'output non è in una riga. Una delle sfide consiste nel sostituire tutte le newline in virgola e 1 spazio eccetto l'ultima riga. Non dovrebbe esserci una virgola dopo l'ultima. – Jaycee

+0

Sì, il mio script si comporta esattamente come grep -o, poiché ora mi rendo conto che è un compito che lascerò a te per il resto dello script. – anubhava

2

Edit: Ho modificato il mio algoritmo, dal momento che avevo trascurato di prendere in considerazione l'intestazione e il piè di pagina (pensavo che fossero solo a nostro vantaggio).

sed, in base alla sua progettazione, accede a ogni riga di un file di input e quindi esegue espressioni su quelle che corrispondono ad alcune specifiche (o nessuna). Se stai adattando il tuo script a un certo numero di righe, stai sicuramente facendo qualcosa di sbagliato! Non ti scriverò una sceneggiatura visto che sono compiti a casa, ma l'idea generale per un modo di procedere è scrivere uno script che faccia quanto segue. Pensa all'ordinamento come all'ordine in cui dovrebbero essere le cose in un copione.

  1. Salta le prime tre righe utilizzando d, che elimina lo spazio del motivo e passa immediatamente alla riga successiva.
  2. Per ogni riga che non è una riga vuota, effettuare le seguenti operazioni. (Sarebbe tutto in un singolo insieme di parentesi graffe.)
    1. sostituire tutto dopo e compresa la prima virgola (;) con una virgola-e-spazio ("") utilizzando il comando s (sostituto).
    2. Aggiungere lo spazio modello attuale a hold buffer (vedere H).
    3. Eliminare lo spazio modello e passare alla riga successiva, come nel passo 1.
  3. Per ogni linea che arriva a questo punto del copione (dovrebbe essere la prima riga vuota), recuperare il contenuto del lo spazio di attesa nello spazio del modello. (Questo sarebbe dopo le parentesi graffe sopra.)
  4. Sostituire tutte le nuove righe nello spazio modello con niente.
  5. Quindi, sostituire l'ultima virgola e spazio nello spazio modello con nulla.
  6. Infine, chiudere il programma in modo da non elaborare più righe. Il mio script ha funzionato senza questo, ma non sono sicuro al 100% perché.

Detto questo, questo è solo un modo per farlo. sed offre spesso vari modi di varia complessità per eseguire un'attività. Una soluzione che ho scritto con questo metodo è lunga 10 righe.

Come nota, non mi preoccupo di eliminare la stampa (con -n) o di stampare manualmente (con p); ogni linea è stampata di default. Il mio script viene eseguito in questo modo:

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

@Jaycee Con quale parte di quanto sopra hai problemi? Mi piacerebbe migliorare la mia spiegazione se posso! –

+0

ciao, Dan, grazie per il suggerimento.Per il primo passaggio, otterrei tutti i simboli con una virgola e uno spazio. Ma sto avendo problemi a fare il 2 ° passo. Come ottengo ogni riga che non è l'ultima riga? Tecnicamente, MMM non è l'ultima riga. ============ è l'ultima riga. Sono così confuso e davvero non so come procedere. Potresti per favore elaborare un po 'di più ??? Grazie mille per il vostro aiuto! – Jaycee

+0

Posso ottenere l'ultimo come segue: /[0-9] $/{N N s/\ (. * \);. *;. * \ N \ n \ = */\ 1/gp } – Jaycee

0

Questo potrebbe funzionare per voi:

sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;q};d' stocks 
  • Noi non vogliamo le intestazioni quindi cerchiamo di eliminarli. 1d
  • Tutti gli elementi di dati sono delimitati da ; così concentriamoci su quelle linee. /;/
  • Delle cose di lassù eliminare tutto dal primo ; fino alla fine della linea e poi roba via nello spazio di attesa (HS) {s/;.*//;H}
  • Quando si arriva all'ultima riga, sovrascrivere con le HS utilizzando il comando g, elimina la prima nuova riga (generata dal comando H), sostituisce tutte le righe nuove successive con una virgola e uno spazio e stampa ciò che è rimasto. ${g;s/.//;s/\n/, /g;q}
  • Elimina tutto il resto d

Ecco una sessione di terminale che mostra la raffinatezza incrementale di costruire 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 
Problemi correlati