Sed può sostituire i nuovi caratteri di linea?


43

C'è un problema con sed e il nuovo personaggio di linea?
Ho un file test.txt con i seguenti contenuti

aaaaa  
bbbbb  
ccccc  
ddddd  

Quanto segue non funziona:
sed -r -i 's/\n/,/g' test.txt

So che posso usare trper questo, ma la mia domanda è perché non sembra possibile con sed.

Se questo è un effetto collaterale dell'elaborazione del file riga per riga, sarei interessato al perché ciò accada. Penso che greprimuova nuove linee. Sed fa lo stesso?


1
In questo caso sed potrebbe non essere lo strumento migliore da usare (es. "Tr"). Ci sono strumenti che sono più intuitivi, più facili da leggere / mantenere, con prestazioni migliori (specialmente su big data) ecc ... Non usare il martello per inserire le viti (anche se funziona). Puoi trovare un confronto su: http://slash4.de/blog/python/sed-replace-newline-or-python-awk-tr-perl-xargs.html
omoser

2
traggiungerebbe un finale ,e genererebbe una riga non terminata. La cosa migliore è usare pasteinvece:paste -sd , test.txt
Stéphane Chazelas il

Risposte:


49

Con GNU sede fornito POSIXLY_CORRECTnon è nell'ambiente (per input a linea singola):

sed -i ':a;N;$!ba;s/\n/,/g' test.txt

Da https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n :

  1. creare un'etichetta tramite :a
  2. aggiungere la riga corrente e successiva allo spazio del modello tramite N
  3. se siamo prima dell'ultima riga, passa all'etichetta creata $!ba( $!significa non farlo sull'ultima riga (poiché dovrebbe esserci un'ultima riga finale)).
  4. infine la sostituzione sostituisce ogni nuova riga con una virgola sullo spazio del modello (che è l'intero file).

Questo sembra indicare che il problema è che sed legge riga per riga, ma non riesco a capire perché sia ​​un problema. Potrebbe semplicemente leggere la riga e sostituire il nuovo carattere di riga (o l'ultimo carattere) con un,
Jim

1
@jim Sembra che non sia nel buffer da abbinare, ma non sono fluente con sed, forse qualcun altro può far luce su questo. Penso che dovresti estendere la tua Q con quelle informazioni specifiche, quindi le persone hanno maggiori probabilità di leggerlo e, si spera, di rispondere.
Anthon,

Ciò si traduce inba: Event not found
krb686,

@ krb686 Che cos'è il "Questo" a cui ti riferisci? Hai eseguito il sedcomando sopra con quelle opzioni esatte? Su quale test.txt file? Con quale versione di sed(provare sed --version)?
Anthon,

@Anthon Mi dispiace, penso di voler dire "il". Ho letto un altro post SO che mi ha informato che csh mi richiede di sfuggire al !. È interessante notare che ancora non ha funzionato per me e ho finito per dover evitare il doppio !nella mia .cshsceneggiatura. Quindi non ho davvero un problema al momento, ma sai perché potrebbe essere? Ciò che ha funzionato per me è statosed :a;N;$\\!ba;s/\n/ /g'
krb686,

17

Funziona con GNU sed:

sed -z 's/\n/,/g' 

-z è incluso dal 4.2.2

NB. -zcambia il delimitatore in caratteri null ( \0). Se l'input non contiene caratteri null, l'intero input viene trattato come una riga singola. Questo può venire con i suoi limiti .

Per evitare la sostituzione della nuova riga dell'ultima riga, è possibile cambiarla nuovamente:

sed -z 's/\n/,/g;s/,$/\n/'

(Che è di sednuovo la sintassi GNU , ma non importa poiché l'intera cosa è solo GNU)


3
Questo sostituirà anche la nuova riga finale che potrebbe non essere ciò che OP vuole ... confrontare il risultato con la soluzione di mikeserv .
don_crissti,

7

Dal sito Web di Oracle:

L'utilità sed funziona leggendo sequenzialmente un file, riga per riga, in memoria. Esegue quindi tutte le azioni specificate per la linea e rimette la linea in memoria per eseguire il dump sul terminale con le modifiche richieste apportate. Dopo che tutte le azioni sono state eseguite su questa riga, legge la riga successiva del file e ripete il processo fino al termine del file.

Fondamentalmente ciò significa che, poiché sed sta leggendo riga per riga, il carattere di nuova riga non corrisponde.

La soluzione da https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n è:

sed ':a;N;$!ba;s/\n/,/g'

o, in una versione portatile (senza ;concatenare dopo le etichette dei segni di salto)

sed -e ':a' -e 'N;$!ba' -e 's/\n/,/g'

Una spiegazione su come funziona è fornita su quella pagina.


Ho usato una forma modificata di questo per analizzare i log VPN e mettere le informazioni "autenticate" e il timestamp sulla stessa riga. Saluti!
user208145

Nota che quella sintassi è specifica per GNU, e anche con GNU sed, se POSIXLY_CORRECT è nell'ambiente e l'input ha solo una linea, non ci sarà output.
Stéphane Chazelas,

5

sedrimuove sempre la \newline finale appena prima di popolare lo spazio modello, quindi ne aggiunge una prima di scrivere i risultati del suo script. Una \newline può essere trovata nello spazio modello con vari mezzi, ma mai se non è il risultato di una modifica. Questo è importante: le \newline nello sedspazio dei pattern riflettono sempre un cambiamento e non si verificano mai nel flusso di input. \nLe ewline sono l'unico delimitatore su cui un utente sedpuò contare con input sconosciuti.

Se vuoi sostituire tutte le \nlinee di posta elettronica con virgole e il tuo file non è molto grande, puoi fare:

sed 'H;1h;$!d;x;y/\n/,/'

Ciò aggiunge ogni riga di input al hvecchio spazio - tranne il primo, che invece sovrascrive il hvecchio spazio - seguendo un \ncarattere di ewline. Quindi delimina ogni riga non l' $!ultima dall'output. Sull'ultima riga Hvengono xcambiati gli spazi vecchi e quelli del motivo e tutti i \ncaratteri della ewline lo sonoy/// tradotti in virgole.

Per file di grandi dimensioni questo genere di cose è destinato a causare problemi sedal buffer sui limiti di linea, che può essere facilmente traboccato da azioni di questo tipo.


2

In alternativa, puoi utilizzare una sintassi leggermente più semplice:

sed ':a;N;s/\n/,/g;ba'

... cambiando semplicemente l'ordine delle sequenze.


3
Ma esegue il scomando per ogni riga di input su uno spazio modello che è sempre più grande.
Stéphane Chazelas,

1

C'è un po 'di sed sed magic qui. E alcuni punti positivi sollevati sul trabocco dello spazio del modello. Adoro usare sed anche quando non è il modo più semplice, perché è così compatto e potente. Tuttavia ha i suoi limiti e, per grandi quantità di dati, lo spazio del modello dovrebbe essere non aggressivo.

GNU dice questo:

Per coloro che desiderano scrivere script sed portatili, tenere presente che alcune implementazioni sono note per limitare le lunghezze di linea (per lo spazio di pattern e hold) non superiori a 4000 byte. La norma posix specifica che le implementazioni sed conformi devono supportare almeno 8192 byte di linee. GNU sed non ha limiti integrati sulla lunghezza della linea; fintanto che può malloc () più memoria (virtuale), puoi alimentare o costruire linee quanto vuoi.
Tuttavia, la ricorsione viene utilizzata per gestire sottoproprietà e ripetizione indefinita. Ciò significa che lo spazio di stack disponibile può limitare la dimensione del buffer che può essere elaborato da determinati modelli.

Non ho molto da aggiungere, ma vorrei indicarti la mia guida per sed . È eccellente http://www.grymoire.com/Unix/Sed.html

ed ecco la mia soluzione:

for i in $(cat test.txt); do echo -n $i','; done; echo '' >> somewhere

bene funziona



-1

Supponiamo che tu voglia sostituire le nuove righe con \n. Volevo farlo, quindi ecco cosa ho fatto:

(echo foo; echo bar; echo baz) | sed -r '$!s/$/\\n/' | tr -d '\n' 
# Output: foo\nbar\nbaz

Ecco cosa fa: per tutte le righe tranne l'ultima , aggiungi \n. Quindi, eliminare le nuove righe con tr.


-rè disponibile solo in GNU sed, non in BSD.
Kenorb,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.