Newsgroup: it.comp.os.linux.iniziare
Titolo: Stampare ennesimo paragrafo di un file di testo con sed/awk (bash)

Ho un breve file di testo, con piccoli poaragrafi di 2-4 righe intervallati da
una riga vuota.
 
Vorrei, usando una variabile, stampare il paragrafo ennesimo di quella
variabile.
 
Se la variabile contiene 3, vorrei stampare solo il terzo paragrafo.
 
Con sed, stampare il paragrafo contenente una specifica parola, è semplice:
 
# print paragraph if it contains AAA (blank lines separate paragraphs)
 sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;'
 
 
Ma stampare l'ennesimo paragrafo indicato da una variabile ?
 
 
-- 
The future is wonderful, the future is terrifying.
The future? But will we still be there?

 Persed
let NUM="0"
for j in $(cat Righe); do
let NUM="$NUM + 1"
echo -n "s/^$j$/" >> Persed
echo "\nþ$NUM/" >> Persed
done
 
# 5. Adesso le numeri cristianamente, aggiungendo la riga vuota finale:
sed -f Persed Park > $DEST
echo >> $DEST
 
# Usage: Mostra le righe di comando per grep sed awk.
# $1: file d'input, se LISCIO -> input = /tmp/Orig
----------------- fine script --------------
 
Il risultato e' questo (cambia il Thorn maiusclo se non ti va):
-------------------------------
 
þ1
aaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaa
 
þ2
bbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbb
 
þ3
ccccccccccccccccccccccccc
 
þ4
ddddddddddddddddd
ddddddddddddddddd
ddddddddddddddddd
ddddddddddddddddd
 
----------------------------------
 
 
Ciao e fammi sapere
 
 
Postilla. Chiaramente per leggere basta dare "pippo 2" con uno script
dove c'e':
awk /þ$1$/,/^$/ Dest
 
e ti da':
þ2
bbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbb
 
 
-- 
Yoda
 

 ----------------- fine script --------------
 
-- 
Yoda
 

 Se ti puo' andare guarda questo artigianale.
 
MMMMMM!
Arzigogolato, veramente!
 
Nella postilla sarebbe da correggere in:
 
awk /þ$1$/,/^$/ Dest | sed '1d'
 
e metterla direttamente alla fine dello script che viene chiamato direttamente
col numero di paragrafo richiesto
 
Sarebbe più corretto se si riuscisse a farlo senza creare un file intermedio...
 
Però, forse forse manipolando il tuo script...
Domani, o meglio in mattinata, visto l'orario, ci do una occhiata...
 
 
Per la cronaca:
 
Anni fa, feci un file con le varie sign da mettere in firma ai miei messaggi,
si può vedere l'effetto se si guardano i miei post degli anni passati.
Era basato su un contatore in cron, poi ogni paragrafo era identificato da un
numero sequenziale.
Ad ogni chiamata di cron, uno script incrementava il contatore, poi estraeva il
paragrafo con quel numero e lo metteva nel file della firma.
Il problema era che se volevo modificare i vari paragrafi (ossia le firme)
dovevo aggiornare tutta la numerazione.
In pratica il tuo file Dest ritorna al mio approccio iniziale di anni fa, ossia
numerare con un contrassegno univoco i paragrafi.
 
 
 
-- 
Tema: Il mestiere di tua madre.
Mia madre è una professionista che lavora nella camera da letto.
Riceve moltissimi clienti, anche di notte, che poi se ne vanno sempre
soddisfatti. Guadagna molto bene e non deve pagare le tasse.

 Arzigogolato, veramente!
 
LOL vero!
 
Quest'altro non scrive nulla, mi sembra buono ciao
 
---- inizio script --------------
#!/bin/bash
 
# INIZIALIZZA
ORIG='/tmp/Orig'
VUOTE="0 $(sed -e /^$/= -e /^$/d $ORIG \
| grep -E "^[[:digit:]]$|^[[:digit:]][[:digit:]]$")"
NUM="$(echo $VUOTE | awk '{print NF}')"
 
# HELP
if [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "" ]; then
echo -e "\033[33m"Numeri di paragrafo validi: 1..$NUM "\033[m"
exit 0
fi
 
# USCITA
CONTROLLO="$(echo $1 | tr -d '[[:digit:]]')"
if [ -z "$CONTROLLO" ]; then
 
if [ "$1" -le "$NUM" ]; then
let INIZ="$(echo $VUOTE | awk -vUNO="$1" '{print $UNO}') + 1"
awk "(NR==$INIZ)",/^$/ $ORIG
else
echo -e "\033[33m"Numeri di paragrafo validi: 1..$NUM - esco! "\033[m"
fi
 
else
echo -e "\033[1;31m"Input \"$1\" NON valido - esco! "\033[m"
fi
---------------- fine script ------
 
-- 
Yoda
 

Addi' 08 set 2018 04:50:37, Yoda scrive:
 
--snip--
 
L'ho modificato (solo nella sezione INIZIALIZZA) per scegliere
eventualmente il file di input con $2; per un qualsiasi numero
di righe vuote (prima arrivava solo fino a 99); per tener conto
in automatica se e' vuota o no la prima riga.
 
Su un testo di guida ai film (a me serviva per quello), con piu'
di 35 mila paragrafi (righe vuote), risulta perfetto e soprattutto
velocissimo: l'help ha impiegato meno di mezzo secondo.
Lo riporto tutto.
 
---- inizio script --------------
#!/bin/bash
 
# INIZIALIZZA
if [ -z "$2" ]; then
ORIG='/tmp/Orig'        # Default, settare solo qui
else
ORIG="$2"
fi
PRIMAVUOTA="$(awk NR==1 $ORIG | grep "^$")"
if [ -n "$PRIMAVUOTA" ]; then
ZERO="0 "
fi
VUOTE="$ZERO$(sed -e /^$/= -e /^$/d $ORIG \
| grep "^[[:digit:]][[:digit:]]*$")"
NUM="$(echo $VUOTE | awk '{print NF}')"
 
# HELP
if [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "" ]; then
echo -e "\033[33m"Numeri di paragrafo validi: 1..$NUM "\033[m"
exit 0
fi
 
# USCITA
CONTROLLO="$(echo $1 | tr -d '[[:digit:]]')"
if [ -z "$CONTROLLO" ]; then
if [ "$1" -le "$NUM" ]; then
let INIZ="$(echo $VUOTE | awk -vUNO="$1" '{print $UNO}') + 1"
awk "(NR==$INIZ)",/^$/ $ORIG
else
echo -e "\033[33m"Numeri di paragrafo validi: 1..$NUM - esco! "\033[m"
fi
else
echo -e "\033[1;31m"Input \"$1\" NON valido - esco! "\033[m"
fi
 
---------------- fine script ------
 
 
-- 
bye, Yoda
 

 ---- inizio script --------------
 
Diciamo che studiando la prima versione e analizzando questa, ci sarebbe una
soluzione più "pulita".
Innanzi tutto l'idea di contare le righe vuote è perfetta.
Poi, elucubrando, grazie ai tuoi esperimenti, ho costruito un qualcosa di più
semplice...
 
 
Non offenderti, ma mi sei stato veramente utile!
 
 
Innanzi tutto, prendiamo in considerazione gli elementi da valutare:
 
1	Il file inizia con nessuna riga vuota
2	Tra un paragrafo e l'altro ci deve essere una sola riga vuota.
3	Il file può avere nessuna, una o più righe vuote alla fine.
 
In queste condizioni ($1 è il numero del paragrafo da stampare):
 
-----------------cut--------------
#!/bin/bash
echo -e "\n$(cat Orig)\n" |\
awk -v PAR=$1 -v N=0 '
( PAR == N ) { print $0 }
( $0 == "" ) { N = N + 1 }
( N > PAR ) { exit }'
-----------------cut--------------
 
La riga iniziale (echo) serve per assicurarmi che ci sia una riga vuota
all'inizio ed alla fine.
L'ultima riga serve per uscire dopo aver stampato senza analizzare inutilmente
il resto del file.
 
 
 
 
Se poi vogliamo "estrapolare" la soluzione qui sopra per possibili righe vuote
multiple ($1 è il numero del paragrafo da stampare):
 
-----------------cut--------------
#!/bin/bash
echo -e "\n$(cat Orig)\n" |\
awk -v PAR=$1 -v N=0 -v a=0 '
( $0 != "" ) { a = 0 }
( PAR == N && $0 != "" ) { a = 0 ; print $0 }
( $0 == "" && a == 0 ) { N = N + 1 ; a = 1 }
( N > PAR ) { exit }'
-----------------cut--------------
 
 
Se un paragrafo è diviso in più segmenti, basta usare una riga composta da
almeno uno spazio per seprare i singoli segmenti.
 
 
Complimenti per i suggerimenti!
 
 
 
-- 
Tema: Il mestiere di tuo padre.
Mio padre lavora come trasportatore per conto di un calzolaio in fiera.
Tutte le sere ci porta le zoccole nuove e la mattina le ritira usate.

 Complimenti per i suggerimenti!
 
Grazie dei complimenti.. ma guarda che mi piace molto fare script, pero'
io sono a livello del tutto dilettantesco. Potresti postare /tutto/ lo
script, se e' gia' completo, o quando l'avrai completato? Sai mi
servirebbe pure a me, a parte che mi diverto a vedere awk in azione.
 
Per le eventuali righe doppie o triple puoi passarlo per uniq (come ho
detto prima), tanto quelle da eliminare sono contigue. Ma magari lo fa
awk, io non sono cosi' avanti con awk da capirlo al volo da questi
segmenti di codice che hai riportato, li studiero' con calma ciao
 
 
Postilla. Quando lo posti voglio vederlo in azione coi 35 mila paragrafi
della mia guida ariciao
 
 
-- 
Yoda
 

 della mia guida ariciao
 
Visto. Su 35 mila paragrafi ci mette quasi 2 secondi, ma tu ne ha max
qualche decina, quindi e' istantaneo ciao
 
-- 
Yoda
 

 il mio script!
 
Lo script di "test", che usa il tuo file di testo Orig come base per le prove,
con aggiunta il conteggio parametri (lanciandolo senza parametri):
 
-----------------cut-----------------
#!/bin/bash
 
if [ $1 ];then
    echo -e "\n$(cat Orig)\n" |\
    awk -v PAR=$1 -v N=0 -v a=0 '
    ( $0 != "" ) { a = 0 }
    ( PAR == N && $0 != "" ) { a = 0 ; print $0 }
    ( $0 == "" && a == 0 ) { N = N + 1 ; a = 1 }
    ( N > PAR ) { exit }'
else
    echo -e "\n$(cat Orig)\n" |\
    awk -v N=0 -v a=0 '
    BEGIN { FS = ":" } {
    if ( $0 != "" && a == 0 ) N = N + 1
    if ( $0 != "" ) a = 1
    if ( $0 == "" ) a = 0
    }
    END { print "Totale: " N " paragrafi." }'
fi
-----------------cut-----------------
 
 
 
 
Lo scopo finale dell'ambaradan:
-------------------------------
 
_Aggiornamento di uno script creato nel lontano 2008 su una debian wheezy._
 
Dato un file "testo3" composto da paragrafi contenente una frase da mettere
come firma (o tagline) ad un post di news, un file opzionale "incipt" da
mettere sopra il separatore (trattino trattino spazio), creare il file
"Tagline" ed un file opzionale di log (last), contenente la tagline stessa più
la data di craezione.
 
 
Il file "testo3":
    1	inizia con nessuna, una o più righe vuote
    2	i paragrafi sono separati da una o più righe vuote
    3	le righe vuote all'interno di uno stesso paragrafo debbono
        contenere almeno un carattere SPAZIO
    4	deve terminare con nessuna, una o più righe vuote
 
 
Lo script per eseguire questo, viene lanciato in una sessione di cron.
 
Ad ogni esecuzione, increamenta un contatore (counter), e riparte da 1 se il
contatore ha superato il numero max di frasi presenti.
 
Il programma newsreader, legge il file risultante "Tagline" e lo aggiunge
automaticamente ad ogni post inviato.
 
 
Lo script finale:
 
----------------cut-----------------
#!/bin/bash
 
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MYDIR=/home/..../..../xxxxxxx
 
[ -f $MYDIR/counter ] || echo "0" > $MYDIR/counter
 
num=$(echo $(( $(cat $MYDIR/counter) + 1 )) )
NUMPAR=$(
    echo -e "\n$(cat $MYDIR/testo3)\n" |\
    awk -v N=0 -v a=0 '
    BEGIN { FS = ":" } {
    if ( $0 != "" && a == 0 ) N = N + 1
    if ( $0 != "" ) a = 1
    if ( $0 == "" ) a = 0
    }
    END { print N }'
)
 
if [ $num -gt $NUMPAR ]
then
  num=1
fi
 
echo $num > $MYDIR/counter
echo > $MYDIR/Tagline
 
if [ -f $MYDIR/incipt ]
then
    cat $MYDIR/incipt  >> $MYDIR/Tagline
    echo >> $MYDIR/Tagline
fi
 
echo "-- " >> $MYDIR/Tagline
 
echo -e "\n$(cat $MYDIR/testo3)\n" |\
awk -v PAR=$num -v N=0 -v a=0 '
( $0 != "" ) { a = 0 }
( PAR == N && $0 != "" ) { a = 0 ; print $0 }
( $0 == "" && a == 0 ) { N = N + 1 ; a = 1 }
( N > PAR ) { exit }' >> $MYDIR/Tagline
 
date -R > $MYDIR/last
cat $MYDIR/Tagline >> $MYDIR/last
 
----------------cut-----------------
 
 
 
 _____  _   _  _____    _____  _   _  ____  
|_   _|| | | || ____|   | ____|| \ | ||  _ \ 
  | |  | |_| ||  _|     |  _|  |  \| || | | |
  | |  |  _  || |___    | |___ | |\  || |_| |
  |_|  |_| |_||_____|   |_____||_| \_||____/ 
 
 
 
 
 
 
 
 
 
 
-- 
Papà, papà, che lavoro fa la mamma?
Non lo so, dice che fa i turni di notte in fiera...
Però guadagna bene, e la mattina porta a casa tanti soldi!

 velocissimo: l'help ha impiegato meno di mezzo secondo.
 
In che senso ti serviva?
Su un elenco lungo, di solito serve una ricerca di parola o di frase...
Una ricerca per posizione...
 
 
-- 
+---------------------------------------------------------------------------+
|  Astronauti: "Houston, abbiamo bisogno di assistenza su un PC!"           |
|  Houston: "Che sistema operativo c'e' installato su quel PC?"             |
|  Astronauti: "Windows XP!"                                                |
|  Houston: "Avete un problema..."                                          |
+-----#10---------------------------Ora di cambiare. Passa a Linux.---------+

     ( N > PAR ) { exit }'
      ( N > PAR ) { exit }' /tmp/Park-$USER
 
.......
.......
.......
rm /tmp/Park-$USER
 
Ciao
 
Posilla. Non capisco perche':
BEGIN { FS = ";" } {
invece di:
BEGIN {} {
 
-- 
Yoda
 

 BEGIN {} {
 
Rimasuglio di un copia-incolla di una prova, che mi sono trascinato senza
accorgermi (tanto funzionave lo stesso...). Ho corretto. Grazie.
 
Ah, è già in funzione...
 
 
 
-- 


 Mi provi così?
 
Quasi istantaneo in senso stretto! 13 millesimi di secondo per par. 100
ed invece 231/1000 per il par. 35200. La superiorita' del codice e'
schiacciante - poi me lo guardo bene, adesso ho pochissimo tempo.
 
Il precedente tuo, modificato col file temporaneo se no ci metteva piu'
di 2 secondi, impiega 51/1000 per par. 100 e 237/1000 per il 35200.
 
Il mio, adesso un po' migliorato ma sempre con sed, 389/1000 sempre,
sia per 100 sia per 35000 (perche' gioca sul file-indirizzo delle righe
vuote); vinceva solo sul tuo primo con la memoria ciao
 
-- 
Yoda
 

 Una ricerca per posizione...
 
Si', pero' adesso me lo son fatto con accesso diretto a scelta, vuoi
per parola o frase, vuoi per [*] posizione ciao
 
Postilla. Sai se batto ad esempio uomo mi vengon giu' molti titoli
e allora me li guardo e do il numero di paragrafo ariciao
 
----------
[*] In realta' non e' per posizione, ma il programma tuo m'e' servito
per numerare ogni film-paragrafo. (ad es. Un uomo tranquillo e' u0842)
 
-- 
Yoda