[bash ma non solo] Duplicare Output - Accorpare due input

Postate qui per tutte le discussioni legate a Linux in generale.

Moderatore: Staff

Regole del forum
1) Citare sempre la versione di Slackware usata, la versione del Kernel e magari anche la versione della libreria coinvolta. Questi dati aiutano le persone che possono rispondere.
2) Per evitare confusione prego inserire in questo forum solo topic che riguardano appunto Gnu/Linux in genere, se l'argomento è specifico alla Slackware usate uno dei forum Slackware o Slackware64.
3) Leggere attentamente le risposte ricevute
4) Scrivere i messaggi con il colore di default, evitare altri colori.
5) Scrivere in Italiano o in Inglese, se possibile grammaticalmente corretto, evitate stili di scrittura poco chiari, quindi nessuna abbreviazione tipo telegramma o scrittura stile SMS o CHAT.
6) Appena registrati è consigliato presentarsi nel forum dedicato.

La non osservanza delle regole porta a provvedimenti di vari tipo da parte dello staff, in particolare la non osservanza della regola 5 porta alla cancellazione del post e alla segnalazione dell'utente. In caso di recidività l'utente rischia il ban temporaneo.
Rispondi
Avatar utente
joe
Iper Master
Iper Master
Messaggi: 3789
Iscritto il: ven 27 apr 2007, 11:21
Slackware: 15.0
Kernel: 5.15.38
Desktop: dwm

[bash ma non solo] Duplicare Output - Accorpare due input

Messaggio da joe »

Scusate lo strano titolo che ho utilizzato ma non sapevo proprio come spiegarmi.

Rispondendo ad un utente su it.comp.os.linux.iniziare, mi è venuto in mente un quesito che forse potrebbe non essere del tutto insensato.
Vi propongo lo schemino qui sotto:

Codice: Seleziona tutto

                ---> sed_nomi -------> 
              /                        \ 
pdftotoext --                           -----> paste out-nomi out-indirizzi 
              \                        / 
                ---> sed_indirizzi --> 
Il flusso delle operazioni va da sinistra a destra.

Abbiamo un comando iniziale che produce un output in questo caso stampa a video un file di testo.

Questo output viene preso contemporaneamente in consegna da due elaborazioni distinte:
- nella fattispecie una fa il parse di nominativi, producendo come output una colonna di "NOME E COGNOME"
- l'altra elaborazione fa il parse dei corrispondenti indirizzi, producendo una colonna del tipo "INDIRIZZO"

Alla fine questi due output vengono presi e passati contemporaneamente al comando finale "paste" che li utilizza come argomenti per stampare un banale file contenente una cosa tipo: "NOME COGNOME;INDIRIZZO".

Vi aggiungo qui sotto un tentativo di script bash per ottenere lo stesso risultato finale, ma usando un file temporaneo di appoggio.

Codice: Seleziona tutto

#!/bin/bash 
INPUT=$1 
CONVERTED=immigrati.txt 
pdftotext -layout $INPUT $CONVERTED 
paste -d\; \ 
        <(sed -n 's/^[0-9]*\s*\([A-Z].*[A-Z]\)\s\{8,\}\([A-Z].*[A-Z]\)\s*[M,F]$/\1;\2/p' $CONVERTED) \ 
        <(sed -n 's/^Residenza:\s*\([A-Z].*[0-9,a-z,A-Z]\)\s*$/\1/p' $CONVERTED) 
rm $CONVERTED
Come si vede, grazie alle "process substitution":
https://www.gnu.org/software/bash/manua ... bstitution

Si può evitare di stampare gli output dei due comandi di parse su altri due files temporanei.
Ma resta sempre la necessità di appoggiarsi ad un file temporaneo $CONVERTED.
Esiste qualche metodo in bash (ma anche in altri linguaggi) per gestire una procedura tipo quella riportata nello schemino?

PS.
L'esempio riportato voleva solo essere di accompagnamento.
Ciò che mi interessava in questa domanda era lo schema:
- prendo un output
- lo duplico (ma perchè no... lo ennuplo
- prendo gli N output e applico a ciascuno una rielaborazione differente
- prendo gli N output che ne escono e li riutilizzo come input di un'operazione finale


Grazie in anticipo! :)

albatrosla
Packager
Packager
Messaggi: 1339
Iscritto il: sab 27 mar 2004, 0:00
Slackware: current
Desktop: fluxbox.git
Località: Collegno, but made in Friûl
Contatta:

Re: [bash ma non solo] Duplicare Output - Accorpare due input

Messaggio da albatrosla »

In parte la risposta dipende da com'è strutturato il pdf e dunque l'output prodotto da pdftotext.
Lo dico perché c'è la possibilità che si riesca a risolvere tutto con una chiamata di sed cui dare in pasto l'output di pdftotext, che può essere ottenuto in stdout invece che in un file.
Qualcosa del tipo:

Codice: Seleziona tutto

pdftotext -layout $INPUT -| sed [...]
Se vuoi mantenere la stessa struttura e non sei un maniaco dell'efficienza, invece, anche questo dovrebbe funzionare:

Codice: Seleziona tutto

paste -d\; \
        <(sed -n 's/^[0-9]*\s*\([A-Z].*[A-Z]\)\s\{8,\}\([A-Z].*[A-Z]\)\s*[M,F]$/\1;\2/p' $(pdftotext -layout $INPUT -)) \
        <(sed -n 's/^Residenza:\s*\([A-Z].*[0-9,a-z,A-Z]\)\s*$/\1/p' $(pdftotext -layout $INPUT -))
Come dicevo all'inizio, risposte alternative dipendono anche da com'è strutturato l'input. Ad esempio puoi pensare di piazzare tutto l'output di pdftotext in una variabile e far trattare quest'ultima a sed. In questo modo non crei alcun file temporaneo.

Avatar utente
joe
Iper Master
Iper Master
Messaggi: 3789
Iscritto il: ven 27 apr 2007, 11:21
Slackware: 15.0
Kernel: 5.15.38
Desktop: dwm

Re: [bash ma non solo] Duplicare Output - Accorpare due input

Messaggio da joe »

Ma, come vedi, sed nel mio caso va a prendere le righe dove sta il nome e cognome, le rielabora e crea una colonna:

Codice: Seleziona tutto

nome;cognome
nome;cognome
ecc ecc
Poi prende le righe contenenti l'indirizzo, che sta su righe diverse da quelle del nominativo di cui sopra e fa la stessa cosa ottnenedo:

Codice: Seleziona tutto

indirizzo
indirizzo
indirizzo
ecc
Infine incollo le due colonne mettendoci in mezzo un punto e virgola.
Alla fine salta fuori un file CSV con separatore di campo ";".

Codice: Seleziona tutto

nome;cognome;indirizzo
nome;cognome;indirizzo
nome;cognome;indirizzo
Quindi non credo sia possibile fare il parse con un unica istanza di sed. O forse sì anche ma non era questo il senso della domanda...
Sed a parte... volevo sapere come splittare un output da dare in pasto come input a diverse procedure (diciamo pure due per semplicità). E poter in cascata riutilizzare i 2 output delle due diverse procedure come input di un comando finale.

Cercando avevo trovato anche questa discussione in cui si parla di "tee", "file descriptors" e "process substitutions":
https://unix.stackexchange.com/question ... e-commands

Non che ci abbia capito granchè comunque.

La tua soluzione di sostituire il file $CONVERTED con una seconda esecuzione di pdftotext non mi piace, l'avevo valutata anche io, ma a parte l'inefficienza in caso di PDF grandi, dribla la domanda che spero a questo punto di aver spiegato meglio.
In ogni caso grazie della risposta, se ti viene in mente qualcos'altro leggo con piacere. :)

EDIT
il PDF su cui fare qualche prova eventualmente lo trovi qua:
https://drive.google.com/open?id=15KZpx ... YQS44Zv09X

albatrosla
Packager
Packager
Messaggi: 1339
Iscritto il: sab 27 mar 2004, 0:00
Slackware: current
Desktop: fluxbox.git
Località: Collegno, but made in Friûl
Contatta:

Re: [bash ma non solo] Duplicare Output - Accorpare due input

Messaggio da albatrosla »

joe ha scritto:Quindi non credo sia possibile fare il parse con un unica istanza di sed.
Si può, si può, ma...
joe ha scritto:O forse sì anche ma non era questo il senso della domanda...
... ok!
joe ha scritto:Sed a parte... volevo sapere come splittare un output da dare in pasto come input a diverse procedure (diciamo pure due per semplicità). E poter in cascata riutilizzare i 2 output delle due diverse procedure come input di un comando finale.

Cercando avevo trovato anche questa discussione in cui si parla di "tee", "file descriptors" e "process substitutions":
https://unix.stackexchange.com/question ... e-commands

Non che ci abbia capito granchè comunque.
tee è molto semplice. Un "tee" in idraulica è un giunto a "T", quello nel quale da un ramo ne nascono due, e serve proprio a fare uno split dell'output di un comando.
Ad esempio, se esegui:

Codice: Seleziona tutto

echo aa | tee >(sed 's/aa/11/') >(sed 's/aa/22/')
ottieni:

Codice: Seleziona tutto

aa
11
22
Ovvero l'output di echo manipolato dalle due istanze di sed. Questo risponde alla tua esigenza di far lavorare i due sed sullo stesso output (che nel tuo caso sarà generato da pdftotext -layout $INPUT -). Attenzione, l'aa in testa è corretto: viene comunque riprodotto l'output di echo, che può essere ripulito con un successivo passaggio di sed.
Il passaggio successivo è paste, che deve accettare due input in stdin. Per farlo bisogna lanciarlo seguito da due score:

Codice: Seleziona tutto

paste - -
Quindi:

Codice: Seleziona tutto

echo aa | tee >(sed 's/aa/11/') >(sed 's/aa/22/')|sed 1d|paste - -
La cosa curiosa, in tutto questo, è che paste mi restituisce:

Codice: Seleziona tutto

22	11
e non il contrario. Provando con un echo a più righe e non manipolando l'output di tee ottengo comunque una curiosa permutazione degli output.
Ad esempio:

Codice: Seleziona tutto

echo "aa
bb"|tee >(sed "s/a/1/") >(sed "s/b/1/")|paste - -
Mi restituisce:

Codice: Seleziona tutto

aa	bb
aa      1b
1a      bb
In pratica, prima il paste sull'output di echo, poi sul secondo output di tee, dunque su quello del primo tee.
Ora non ho tempo/qualcosa di meglio su cui lavorare, ma direi che le basi per elaborare meglio ci sono.

Avatar utente
joe
Iper Master
Iper Master
Messaggi: 3789
Iscritto il: ven 27 apr 2007, 11:21
Slackware: 15.0
Kernel: 5.15.38
Desktop: dwm

Re: [bash ma non solo] Duplicare Output - Accorpare due input

Messaggio da joe »

In quel modo "paste" prendo l'output finale già ri-accorpato e crea due colonne con i nomi alternati...
Invece io voglio che paste prenda non l'output finale ma come primo argomento solo l'output del primo sed e come secondo argomento il solo output del secondo sed.

Consideriamo il seguente file di input

Codice: Seleziona tutto

$ cat inputfile.txt 
Nome: pippo
Residenza: topolinia
qoiwqh

qgbqògb

ldjndlkjfnbdfl
dlsjnfbskdjnb

Nome: paperino
wgiubhwenròu


bwòeibuò
òjbwnòjn

Residenza: paperopoli
In output vogliamo ottenre

Codice: Seleziona tutto

pippo	topolinia
paperino	paepropoli
Ecco un tentativo raffazzonato al volo:

Codice: Seleziona tutto

$ cat inputfile.txt |tee >(sed -n 's/^Nome:\s//p') >(sed -n 's/^Residenza:\s//p')
Nome: pippo
Residenza: topolinia
qoiwqh

qgbqògb

ldjndlkjfnbdfl
dlsjnfbskdjnb

Nome: paperino
wgiubhwenròu


bwòeibuò
òjbwnòjn

Residenza: paperopoli
joe@localmachine:~/prova-parse$ topolinia
paperopoli
pippo
paperino
Intanto osserverei il fatto che "tee" redirige l'output prima di tutto su standard output. Quella roba a noi non serve e dovremmo buttarla in qualche modo.
Allo stesso tempo l'output del cat viene inviato alle process substitutions e viene rimaneggiato indipendentemente dall'uno e dall'altro comando sed.
Non si capisce perchè viene risputato dopo il prompt... ma va bè.
Aggiungiamoci il "paste - -":

Codice: Seleziona tutto

$ cat inputfile.txt |tee >(sed -n 's/^Nome:\s//p') >(sed -n 's/^Residenza:\s//p') &>/dev/null | paste - -
topolinia       paperopoli
pippo   paperino
Come dicevo il paste non incolla i due output distinti, ma incolla la concatenazione dei due output...
E questo comportamento si vede bene provando una cosa del genere:

Codice: Seleziona tutto

$ echo -e 'a\nb\n1\n2'|paste - -
a       b
1       2
In sostanza il paste coi due trattini non va bene...

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6629
Iscritto il: gio 3 nov 2005, 14:05
Nome Cognome: Emanuele Tomasi
Slackware: 64-current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Re: [bash ma non solo] Duplicare Output - Accorpare due input

Messaggio da targzeta »

albatrosla ha scritto:...
Quindi:

Codice: Seleziona tutto

echo aa | tee >(sed 's/aa/11/') >(sed 's/aa/22/')|sed 1d|paste - -
La cosa curiosa, in tutto questo, è che paste mi restituisce:

Codice: Seleziona tutto

22	11
e non il contrario. Provando con un echo a più righe e non manipolando l'output di tee ottengo comunque una curiosa permutazione degli output.
...
Perché la bash esegue i comandi in maniera asincrona, quindi non si può predire l'ordine.

Emanuele
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama

Rispondi