Rimuovi il carattere di nuova riga solo ogni N righe


16

Durante l'elaborazione del testo, devo rimuovere il carattere di nuova riga ogni due righe.

Testo di esempio:

this is line one
and this is line two
the third and the
fourth must be pasted too

Uscita desiderata:

this is line one and this is line two
the third and the fourth must be pasted too

Ho provato un whileciclo, ma un ciclo while è una cattiva pratica. È possibile farlo usando tro qualsiasi altro comando?


4
Il titolo dice "ogni N righe", ma nella domanda e nell'esempio è "ogni 2 righe". La maggior parte delle risposte funziona solo per N = 2. Stai cercando qualcosa che funzioni per tutte le N?
JigglyNaga,

Questa è la chiave. Tutti hanno risposto per 2 righe ma avrei bisogno di usare N = 3 o N = 4
jomaweb

Risposte:


24

paste(anche una semplice utility POSIX standard come tr) è il tuo strumento per questo.

Supponendo che si desideri sostituire i caratteri di nuova riga con uno spazio anziché rimuoverli come nell'esempio:

paste -d ' ' - - < file

O:

paste -sd ' \n' file

Sostituisci ' 'con '\0'se davvero li vuoi rimuovere.

Per sostituire 2 su 3:

paste -sd '  \n' file

1 su 3, a partire dal secondo:

paste -sd '\n \n' file

E così via.

Un'altra cosa positiva pasteè che non lascerà una linea non terminata. Ad esempio, se si rimuove ogni nuova riga in un file (come con tr -d '\n' < fileo tr '\n' ' ' < file), si finisce senza alcuna linea poiché le righe devono essere terminate con un carattere di nuova riga. Quindi è generalmente meglio usare pasteinvece quello (come in paste -sd '\0' fileo paste -sd ' ' file) che aggiungerà quel carattere di nuova riga finale necessario per avere un testo valido.


11

Con la moderna GNU sed

sed -rz 's/\n([^\n]*\n)/ \1/g' sample.text

E awk

awk '{getline line2;print $0, line2}' sample.text

3
Questo sedapproccio implica l'assorbimento dell'intero file in memoria (a condizione che non contenga byte NUL) e una costosa sostituzione regexp. Non riesco a vedere il vantaggio rispetto sed 'N;s/\n/ /'all'approccio standard .
Stéphane Chazelas,

6

Utilizzare sedper questo come mostrato di seguito:

SHW@SHW:/tmp $ cat a
this is line one
and this is line two
the third and the
fourth must be pasted too

SHW@SHW:/tmp $ sed 'N;s/\n/ /' a -i

SHW@SHW:/tmp $ cat a
this is line one and this is line two
the third and the fourth must be pasted too

4

Un altro modo è usare xargs:

$ < txt xargs -d '\n' -n 2 echo
this is line one and this is line two
the third and the fourth must be pasted too

dove

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

Tuttavia, questa soluzione è piuttosto eccessiva perché echoviene eseguito un processo per ogni riga ... Pertanto, oltre agli esempi di giocattoli, dovrebbe essere preferita una soluzione basata su awk / sed o simili.


1
A seconda della tua echoimplementazione, avrai anche problemi con i caratteri di barra rovesciata o alcune righe che iniziano con -(come --helpo -nenecon GNU echo). Si noti inoltre che -dè un'estensione GNU.
Stéphane Chazelas,

Per evitare problemi echo, puoi usare questo:< txt xargs -d '\n' -n 2 printf -- '%s %s\n'
nyuszika7h

4

Questo è in realtà estremamente semplice in vim. Per unire ogni linea usa il Jcomando, quindi usa il %normcomando per applicarlo su ogni linea contemporaneamente. Per esempio

:%norm J<CR>

(Nel caso in cui non si abbia familiarità con Vim, <CR>significa solo entrare)

Questo funziona anche per unire un numero arbitrario di linee. Ad esempio, unire ogni dieci righe sarebbe

:%norm 9J<CR>

Se non ti senti a tuo agio con vim e preferiresti utilizzarlo come uno strumento da riga di comando, piuttosto che un editor di testo interattivo, potresti fare:

vim myfile -c '%norm J' -c 'wq'

Il downvoter si preoccuperebbe di spiegare cosa posso fare per migliorare questa risposta?
DJMcMayhem,

3
$ awk '{printf "%s%s",$0,(NR%2?" ":"\n")}' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

Ciò stampa ogni riga, $0seguita da uno spazio o una nuova riga a seconda che il numero della riga NR, sia pari o dispari.

L'espressione NR%2?" ":"\n"è un'affermazione ternaria. L'espressioneNR%2 valutata vera (diversa da zero) se il numero di riga è dispari. Nel caso, l'espressione ternaria restituisce uno spazio. Se restituisce false (zero), viene restituita la nuova riga.

Alternativa

Come suggerito da Costas nei commenti:

$ awk '{ORS=(NR%2?" ":RS)}1' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

Qui, l'istruzione ternary NR%2?" ":RSviene utilizzata per restituire uno spazio o il separatore del record di input ( RS, default = newline). Questo valore viene assegnato al separatore record in uscita, ORS. Alla 1fine del comando c'è la stenografia criptica di awk per stampare il record.


Puoi ancora salvare 3 caratteri: le ()parentesi e lo spazio dopo printf;)
maxschlepzig

1
Ternario? Oh! 'NR%2{printf("%s ",$0);next}1'
Costas,

Con la risposta di Maxschlepzig e la dichiarazione ternaria:'{ORS=(NR%2?" ":RS)}1'
Costas,

@Costas Mi piace. Risposta aggiornata con ORSsoluzione.
Giovanni 1024,

2

Soluzione generica, sostituire 5con il numero di righe richieste

$ # eof to ensure last line has newline ending
$ seq 16 | perl -pe 's/\n/ / if ++$i%5 && !eof'
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16

$ # or just use pr
$ seq 16 | pr -5ats' '
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16

1

Puoi usare awkper questo:

$ awk '{c="\n"} NR%2 {c=" "} { printf("%s%s", $0, c) } ' txt

Produce:

this is line one and this is line two
the third and the fourth must be pasted too

dove:

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

Le awkazioni vengono eseguite per ogni riga, la variabile speciale fa $0riferimento alla riga corrente, NRè il numero di riga corrente (a partire da 1). La seconda azione è protetta dall'espressione NR%2, che è l'operazione modulo. Pertanto, c=" "viene eseguito solo se NR%2è vero, ovvero per numeri di riga dispari.

La awksintassi è simile a C, ma alcuni elementi sono facoltativi in ​​alcuni contesti, ad esempio punti e virgola.


La tua cvariabile è ORS:'NR%2{ORS=" "}1;{ORS=RS}'
Costas

0

Utilizzando ed:

$ cat text
this is line one
and this is line two
the third and the
fourth must be pasted too
this is line one
and this is line two
the third and the
fourth must be pasted too

$ ed text <<'END_ED'
g/./s/$/ /\
j
w text.new
END_ED
164
164

$ cat text.new
this is line one and this is line two
the third and the fourth must be pasted too
this is line one and this is line two
the third and the fourth must be pasted too

I edcomandi di modifica, per ogni riga ( gapplica un insieme di comandi di modifica a ogni riga corrispondente all'espressione regolare fornita), aggiungono un carattere spazio alla fine e lo uniscono alla riga successiva. Quindi scrive il testo risultante in un file chiamato text.new.


0

Con Ruby.

Presumo che ogni blocco di nlinee debba essere unito. Supponiamo che n = 3il file di input sia 'infile'e i risultati debbano essere scritti nel file 'outfile'.

Costruisci un file

Ruby -e "File.write 'infile', <<_
> Line 1
> Line 2
> Line 3
> Line 4
> Line 5
> Line 6
> Line 7
> _"

Conferma il contenuto del file

ruby -e "p File.read 'infile'"
  # "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\n"

Rimuovi le nuove righe e scrivi sul file

ruby -e "File.write 'outfile', File.readlines('infile').
  each_with_index { |line,i| line.chomp! unless (i+1)%3==0 }"

Conferma i contenuti

ruby -e "puts File.read 'outfile'"
  # ["Line 1", "Line 2", "Line 3\n", "Line 4", "Line 5", "Line 6\n", "Line 7"]

1
Buona In teoria,ruby è fuori tema su U&L. Ma, dal momento che lo stai usando dalla riga di comando con ruby -e, questo lo rende abbastanza sull'argomento.
grochmal,
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.