astratto
Stampa righe senza newline, aggiungi una nuova riga solo se è presente un'altra riga da stampare.
$ printf 'one\ntwo\n' |
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
Altre soluzioni
Se stessimo lavorando con un file, possiamo semplicemente troncare un carattere da esso (se termina su una nuova riga):
removeTrailNewline () {[[$ (tail -c 1 "$ 1")]] || troncare -s-1 "$ 1"; }
Questa è una soluzione rapida in quanto deve leggere solo un carattere dal file e quindi rimuoverlo direttamente ( truncate
) senza leggere l'intero file.
Tuttavia, mentre si lavora con i dati di stdin (un flusso) i dati devono essere letti, tutti. E, viene "consumato" non appena viene letto. Nessun backtrack (come con troncato). Per trovare la fine di un flusso dobbiamo leggere fino alla fine del flusso. A quel punto, non è possibile tornare indietro nel flusso di input, i dati sono già stati "consumati". Ciò significa che i dati devono essere archiviati in una qualche forma di buffer fino a quando non abbiniamo la fine del flusso e quindi facciamo qualcosa con i dati nel buffer.
La soluzione più ovvia è convertire lo stream in un file ed elaborare quel file. Ma la domanda richiede una sorta di filtro del flusso. Non sull'uso di file aggiuntivi.
variabile
La soluzione ingenua sarebbe quella di catturare l'intero input in una variabile:
FilterOne(){ filecontents=$(cat; echo "x"); # capture the whole input
filecontents=${filecontents%x}; # Remove the "x" added above.
nl=$'\n'; # use a variable for newline.
printf '%s' "${filecontents%"$nl"}"; # Remove newline (if it exists).
}
printf 'one\ntwo' | FilterOne ; echo 1done
printf 'one\ntwo\n' | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done
memoria
È possibile caricare un intero file in memoria con sed. In sed è impossibile evitare la nuova riga finale sull'ultima riga. GNU sed potrebbe evitare di stampare una nuova riga finale, ma solo se il file sorgente è già mancante. Quindi, no, la semplice sed non può aiutare.
Tranne che su GNU awk con l' -z
opzione:
sed -z 's/\(.*\)\n$/\1/'
Con awk (qualsiasi awk), assorbi l'intero flusso e printf
senza la nuova riga finale.
awk ' { content = content $0 RS }
END { gsub( "\n$", "", content ); printf( "%s", content ) }
'
Il caricamento di un intero file in memoria potrebbe non essere una buona idea, potrebbe consumare molta memoria.
Due righe in memoria
In awk, possiamo elaborare due righe per loop memorizzando la riga precedente in una variabile e stampando quella corrente:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
Elaborazione diretta
Ma potremmo fare di meglio.
Se stampiamo la riga attuale senza una nuova riga e stampiamo una nuova riga solo quando esiste una riga successiva, elaboriamo una riga alla volta e l'ultima riga non avrà una riga nuova finale:
awk 'NR == 1 {printf ("% s", $ 0); successivo}; {printf ("\ n% s", $ 0)} '
Oppure, scritto in qualche altro modo:
awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'
O:
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'
Così:
$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
chomp
, poichéchomp
elimina solo al massimo una nuova riga finale.