2013-08-26 14 views
9

Sto provando a convertire un HTML contenente una tabella in un file .csv utilizzando uno script bash.Utilizzare sed o awk per fissare il formato data

Finora ho acomplished le seguenti operazioni:

  1. Converti in formato Unix (con dos2unix)
  2. Rimuovere tutti gli spazi e tabulazioni (con sed 's/[ \t]//g')
  3. rimuovere tutte le righe vuote (con sed ':a;N;$!ba;s/\n//g') (questo è necessario, perché il file HTML ha una riga vuota per ogni cella della tabella ... non è colpa mia)
  4. Rimuovere i tag <td> e <tr> non necessari (con sed 's/<t.>//g')
  5. Sostituire </td> con '' (con sed 's/<\/td/,/g')
  6. Sostituire </tr> con (\n) caratteri di fine linea (con sed 's/<\/tr/\n/g')

Naturalmente, sto mettendo tutto questo in una pipeline. Finora, sta funzionando alla grande. C'è un ultimo passaggio in cui sono bloccato: il tavolo ha una colonna con date, che ha il formato dd/mm/yyyy e mi piacerebbe convertirli in yyyy-mm-dd.

Esiste un modo (semplice) per farlo (con sed o awk)?

campione di dati (dopo tutto sed pipe): risultato

500,2,13/09/2007,30000.00,12,B-1 
501,2,15/09/2007,14000.00,8,B-2 

Expected:

500,2,2007-09-13,30000.00,12,B-1 
501,2,2007-09-15,14000.00,8,B-2 

La ragione per cui ho bisogno di fare questo è perché ho bisogno di importare questi dati a MySQL. Potrei aprire il file in Excel e modificare il formato a mano, ma vorrei saltarlo.

risposta

7

Awk può fare questo compito abbastanza facilmente:

awk ' 
    BEGIN { FS = OFS = "," } 
    { split($3, date, /\//) 
     $3 = date[3] "-" date[2] "-" date[1] 
     print $0 
    } 
' infile 

Produce:

500,2,2007-09-13,30000.00,12,B-1 
501,2,2007-09-15,14000.00,8,B-2 
4
sed "s:,\([0-9]\+\)/\([0-9]\+\)/\([0-9]\+\),:,\3-\2-\1,:" 
4

awk funzionerebbe per questo:

echo 08/26/2013 | awk -F/ '{printf "%s-%s-%s\n",$3,$2,$1}' 

come farebbe uno di questi bash opzioni -solo:

IFS=/ read m d y < <(echo 08/26/2013); echo "${y}-${m}-${d}" 
IFS=/ read m d y <<< "08/26/2013"; echo "${y}-${m}-${d}" 

Se vi capita di usare ksh, dove una subshell non viene utilizzato per l'ultimo componente di una condotta, questo dovrebbe funzionare così:

echo 08/26/2013 | IFS=/ read m d y; echo "${y}-${m}-${d}" 

Nel recente bash, è inoltre possibile utilizzare shopt -s lastpipe in uno script per consentire il funzionamento dell'invocazione precedente, ma non funzionerà sulla riga di comando (grazie a @ mklement0 nei commenti seguenti).

Lascio a voi per capire come integrare con il resto ...

+0

Nice, ma il comando 'read'-based non funzionerà, in quanto' read' viene eseguito in _subshell_ in questo caso; usa 'echo '08/26/2013 '| {IFS =/read m d y; echo "$ {y} - $ {m} - $ {d}"; } 'o ' IFS =/leggi m d y <<< '08/26/2013'; echo "$ {y} - $ {m} - $ {d}" ' – mklement0

+1

@ mklement0 Ah, sì .... ho dimenticato quel piccolo dettaglio. Funzionerebbe in 'ksh', comunque. Un'altra alternativa sarebbe 'IFS =/read m d y <<(echo 08/26/2013)' per evitare la subshell (anche se 'echo' si troverebbe in una subshell). – twalberg

+0

Buoni punti, anche se '<<<' è probabilmente il più efficiente qui. In Bash v4.2 + puoi anche usare 'shopt -s lastpipe' (ma solo negli script). Posso suggerire di aggiornare la risposta con una delle soluzioni operative? – mklement0

7
sed -E 's,([0-9]{2})/([0-9]{2})/([0-9]{4}),\3-\2-\1,g' 
+0

Inserito in questo come esempio di primo passaggio per ottenere le cose e ha funzionato alla lettera! Grazie @ash! – Matthew

1

correzione awk assumere cercate aaaa-mm-gg (non aaaa-gg-mm)

echo 26/08/2013 | awk -F/'{printf "% s-% s-% s \ n", $ 3, $ 1, $ 2}'

2

Finora tutte le risposte sono specifiche del caso specifico dell'OP. Ecco un approccio più generale, in esecuzione (GNU, per -d opzionale) date attraverso awk:

awk 'BEGIN{FS=","} 
    { 
     "date -d\"" $3 "\" +%Y-%m-%d" | getline mydate; 
     print $1 "," $2 "," mydate "," $4 "," $5 "," $6 
    }' 

Naturalmente questo approccio funziona come è solo se il formato della data di ingresso è gestito da date. AFAICS questo non è il caso di dd/mm/yyyy, sfortunatamente. Si può provare other commands di date (non testato).

Modifica: ha implementato il commento di mklement0.

Edit2: In realtà questo non funziona con mawk, che è l'implementazione predefinita di Debian awk. La soluzione ovvia è installare gawk quando possibile.

+1

++, ma dovresti menzionare che _GNU_ 'date' è richiesto a causa di' -d'; allo stesso modo, '| &' è un'estensione GNU Awk, ma non è effettivamente necessaria qui: '|' farà, il che fa funzionare tutto Awks. Infine, ti suggerisco di utilizzare spazi tra le stringhe concatenati, sia per chiarezza visiva che per mostrare che la concatenazione di stringhe in Awk funziona in modo diverso da rispetto alla shell; ad es., "date -d" "$ 3" '+% Y-% m-% d "' (Ho anche aggiunto virgolette singole per difendermi dal campo con spazi incorporati). – mklement0

+1

@ mklement0: Grazie per i tuoi suggerimenti, ho modificato la risposta. Le virgolette singole non lo faranno, le ho sostituite con virgolette doppie evasive. –

+1

Grazie per aver aggiornato la risposta, e grazie per aver colto l'errore delle mie virgolette (solo per enunciarlo esplicitamente: le virgolette singole non possono essere usate all'interno dello script Awk, perché lo script nel suo complesso è quotato in una virgola). – mklement0