Newsgroup: it.comp.os.linux.iniziare
Titolo: Intercettare e modificare linee che non contengono una data regexp

Scusate la lunghezza del titolo...
Allora, spiego il problema nella pratica:
ho un file di testo tipo il seguente (ne riporto solo poche righe):
-----------------
30/12/2015 20:00
30/12/2015 21:00
30/12/2015 22:00
30/12/2015 23:00
31/12/2015 00:00
31/12/2015 01:00
31/12/2015 02:00
31/12/2015 03:00
31/12/2015 04:00
31/12/2015 05:00
31/12/2015 06:00
31/12/2015 07:00
31/12/2015 08:00
31/12/2015 09:00
31/12/2015 10:00
-----------------
 
Si vuole rielaborare come segue:
-----------------
 
 
 
 
31/12/2015 00:00
 
 
 
 
 
 
 
 
 
 
-----------------
 
In soldoni si è sostituita una riga vuota a ciascuna linea che *non*
conteneva il pattern "00:00".
 
Quindi:
 
1- abbiamo un pattern ("00:00")
 
2- possiamo intercettarlo con la regexp seguente: "^.*00\:00.$"
   che troverà tutte le linee che contengono l'espressione regolare
   di cui sopra. (testato con sed 's ecc)
 
3- però vogliamo intercettare le linee che *non* lo contengono,
   pertanto ho pensato che invertendo in qualche modo la regexp
   di cui sopra si possa raggiungere lo scopo.
 
 
Come si può fare ad esempio con sed?
 
Ho è un pezzo che sto cercando in rete ma mi sto un po' perdendo
inciampando spesso in risultati che riportano modi per eliminare
linee vuote e roba simile...
 
Ho letto anche dell'operatore "!?" o giù di lì ma non capisco come
farlo funzionare, alcuni esempi non rendono il risultato che serve.
 
 
Grazie in anticipo!  :)
 
 
 
 
PS.
"grep -v regexp " in un caso del genere non funziona perchè elimina
completamente le linee *non* corrispondenti, invece a me serve poterle
solamente "svuotare". Spero di aver reso l'idea.

 Come si può fare ad esempio con sed?
 
Senza sapere ne leggere ne scrivere:
https://www.gnu.org/software/sed/manual/sed.txt
 
 
capitolo 3.5
 
Ciao Jack

 capitolo 3.5
 
 
Ok, si lì spiega come usare sed per sostituire qualcosa
con qualcos'altro, la sintassi in sintesi è la seguente:
 
sed 's/REGEXP/REPLACEMENT/FLAGS' file
 
 
Per rifarmi in parte al mio caso se ad esempio volessi
intercettare le righe contenenti '00:00' e sostituirle
con una riga vuota ecco che avrei:
 
sed 's/^.*00:00.*$//' file
 
in pratica ho posto:
 
REGEXP:        "^.*00:00.*$"
REPLACMENT:    ""
FLAGS:         ""
 
Ora magari dico una scemata, però se trovassi l'espressione
regolare corrispondente al "NOT" logico della "REGEXP" di cui
sopra, avrei risolto.
 
Con il comando "d" (delete) di sed si potrebbe sfruttare la
possibilità seguente (vado a memoria):
 
sed '/REGEXP/!d' file
 
che tradotto:
 
sed '/00:00/!d' file
 
Questo comando intercetta efettivamente la "regexp invertita"
ma è poco versatile dal punto di vista della modifica, perchè
si limita a rimuovere completamente le righe che *non* contengono
il pattern.
Il risultato che si ottiene è tipo:
-----------------
01/01/2015 00:00
02/01/2015 00:00
03/01/2015 00:00
04/01/2015 00:00
05/01/2015 00:00
06/01/2015 00:00
07/01/2015 00:00
08/01/2015 00:00
09/01/2015 00:00
10/01/2015 00:00
-----------------
 
In pratica mancano le righe vuote... E questo ridultato non mi è
utile...
 
Grazie comunque per il momento.
Ciao!

 
 
Forse ci siamo:
http://tdistler.com/2009/04/17/
 
     sed-regular-expression-find-lines-not-matching-a-string
 
Dove viene proposta il seguente comando (guardare l'esempio):
----
sed -i ‘/^[ \t]*\/\/.*/! { s/STRING1/STRING2/ }’
----
 
 
In pratica dice a sed:
 
- intercetta le righe che non soddisfano la regex
 
  sed '/REGEXP/!'
 
- e per ognuna effettua la sostituzione specificata
 
  's/REGEXP2/REPLACEMENT/'
 
 
Applicando tutto al mio caso:
 
sed '/00:00/!s/^.*$//'  file
 
In pratica:
- trova tutte le linee che non contengono 00:00
- e sostituiscine tutto il contenuto con niente
- in questo modo le "new_lines" restano
 
ed ecco cosa esce:
-----------------------------------------------------
blabla file |sed '/00:00/!s/^.*$//'|cat -n|head -n25
 
     1  01/01/2015 00:00
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25  02/01/2015 00:00
-------------------------
 
Giustamente visto che quella roba lì sono "data e ora" e
che ogni riga contiene un'ora "piena" ecco che dopo 24 righe
mi aspetto sia cambiata data e venga riscritto l'orario, che
sarà sempre 00:00.
 
 
Insomma, tutto risolto.
Mi sarei aspettato più visibilità per un esempio del genere
che mi pare molto utile in generale in quanto la necessità di
"invertire un'espressione regolare" può capitare facilmente.
Invece come dicevo all'inizio, le prime ricerche in google
non mi hanno portato dove volevo...
 
Alla prossima!  :)


Consideriamo un caso simile ma un po' più complesso:
------------------------------------
01/01/2015 00:00;01/01/2015 01:00;0
01/01/2015 01:00;01/01/2015 02:00;0
01/01/2015 02:00;01/01/2015 03:00;0
01/01/2015 03:00;01/01/2015 04:00;0
01/01/2015 04:00;01/01/2015 05:00;0
01/01/2015 05:00;01/01/2015 06:00;0
01/01/2015 06:00;01/01/2015 07:00;0
01/01/2015 07:00;01/01/2015 08:00;0
01/01/2015 08:00;01/01/2015 09:00;0
------------------------------------
 
Questa volta abbiamo più campi separati dal punto e virgola:
il prim è un tempo iniziale, il secondo un tempo finale, il
terzo un valore relativo a quell'intervallo temporale.
 
Si vuole rielaborare il file in modo da ottenere in uscita
una cosa del tipo:
 
------------------------------------
     0.000     01/01/2015 00:00
     0.000
     0.000
     0.000
     0.000
     0.000
     0.000
     0.000
     0.000
------------------------------------
 
Cioè si crea un file di dati con in prima colonna dei dati,
in questo caso di valore nullo (sono misurazioni orarie) e
in seconda colonna viene stampato semplicemente la data
e l'ora di inizio ad ogni mezzanotte.
In modo che chi mette mano al file di dati ha un riferimento
temporale e capisce facilmente a quale data si riferiscono le
misurazioni che sta controllando.
 
Sono riuscito ad ottenere un risultato già soddisfacente con
uno script bash che usa "cut" "sed" "printf" "paste" e qualche
file di appoggio temporaneo.
 
L'idea è:
 
0- rielaboro il file di input, per come mi viene fornito
   devo tagliarne le prime 5 righe e le ultime 3, in modo
   da ottenere la forma dell'input nell'esempio sopra.
---------------------
sed '1,6d' $SERVEDFILE | tac | sed '1,3d' |tac > "${TMPST}"
---------------------
 
1- estraggo il campo "valori misurati" lo riformatto nel
   formato desiderato (in questo caso '%10.3f\n') e salvo
   su un file temporaneo (LC_NUMERIC imposta il separatore
   decimale al "punto" piuttosto che alla "virgola"):
---------------------
export LC_NUMERIC="en_US.UTF-8"
printf '%10.3f\n' $(cut -d\; -f3 "$TMPST") > "$TMPVAL"
---------------------
 
2- estraggo il campo dei tempi, ne cancello le righe che non
   servono e salvo l'output su un secondo file temporaneo:
---------------------
cut -d\; -f1 "$TMPST" | sed '/00:00/!s/^.*$//' > "$TMPTIME"
---------------------
 
3- incollo le due colonne:
---------------------
paste "$TMPVAL" "$TMPTIME" > "$OUTFILE"
---------------------
 
 
Considerazioni:
 
Tutto questo è alla fine abbastanza semplice, però il
file iniziale viene letto, editato, riletto rieditato,
ci si appoggia a files temporanei ecc...
 
Avendo a che fare con file di ingresso aventi parecchie
righe, anche ottimizzarne l'efficenza potrebbe essere
interessante.
Probabilmente a questo proposito ci sono migliorie da
apportare al mio tentativo descritto sopra.
 
Che cosa ne pensate?
Si possono migliorare le prestazioni di sta roba in
qualche modo?
Quali strumenti più efficienti si potevano usare?
awk?
perl?
cos'altro?
 
Qualche vostro commento mi sarebbe senz'altro utile.
grazie in anticipo!   :)

 grazie in anticipo!   :)
 
 
Ma io sono molto inesperto.
 
 
-- 
AIOE ³¿³