Come inserire una nuova riga davanti a un modello?


138

Come inserire una nuova riga prima di un modello all'interno di una linea?

Ad esempio, questo inserirà una nuova riga dietro il modello regex.

sed 's/regex/&\n/g'

Come posso fare lo stesso ma davanti allo schema?

Dato questo file di input di esempio, il modello da abbinare è il numero di telefono.

some text (012)345-6789

Dovrebbe diventare

some text
(012)345-6789


1
@NilsvonBarth, perché una domanda semplice è una cattiva domanda?
Josh,

Risposte:


177

Funziona in bashe zsh, testato su Linux e OS X:

sed 's/regexp/\'$'\n/g'

In generale, per $seguito da una stringa letterale tra virgolette singole bashesegue la sostituzione della barra rovesciata in stile C, ad esempio $'\t'viene tradotta in una scheda letterale. Inoltre, sed vuole che il tuo newline letterale sia sfuggito a una barra rovesciata, da cui il \prima $. E infine, il simbolo del dollaro stesso non dovrebbe essere citato in modo che sia interpretato dalla shell, quindi chiudiamo la citazione prima di $e quindi la riapriamo.

Modifica : come suggerito nei commenti di @ mklement0, funziona anche questo:

sed $'s/regexp/\\\n/g'

Quello che succede qui è: l'intero comando sed è ora una stringa in stile C, il che significa che la barra rovesciata che sed richiede di essere posizionata prima che la nuova riga letterale debba ora essere evasa con un'altra barra rovesciata. Anche se più leggibile, in questo caso non sarai in grado di fare sostituzioni di stringhe di shell (senza renderlo di nuovo brutto).


7
Questo mi dà "newline senza escape all'interno del modello sostitutivo" su OSX.
Matt Gibson,

@Matt Gibson è molto strano perché "newline senza escape" viene dato solo quando si dispone di una nuova riga reale senza una barra rovesciata all'interno del modello di sostituzione. Il mio codice sopra funziona, infatti, anche in alcune altre shell, ad esempio zsh, ksh.
Mojuba,

3
@ Matt Gibson ... o se dimentichi la barra rovesciata prima di '$' \ n nel mio codice.
Mojuba,

7
Come scritto, queste espressioni sostituiscono completamente la regex con una nuova riga, anziché inserire una nuova riga nel mezzo di una riga esistente come richiesto. Ecco come ho usato una forma modificata di questa risposta per inserire un ritorno a capo tra due modelli abbinati: sed '\(first match\)\(second match\)/\1\'$'\n''\2/g'. Nota le due virgolette singole dopo il \ n. Il primo chiude la $sezione " " in modo che il resto della linea non ne sia influenzato. Senza quelle citazioni, il \ 2 è stato ignorato.
David Ravetti,

12
Un'altra opzione è quella di utilizzare una singola stringa ANSI tra virgolette C : sed $'s/regexp/\\\n/g'che migliora la leggibilità: l'unica avvertenza è che è necessario raddoppiare tutti i \ caratteri letterali .
mklement0

43

Alcune delle altre risposte non hanno funzionato per la mia versione di sed. Cambiare posizione &e \nha funzionato.

sed 's/regexp/\n&/g' 

Modifica: questo non sembra funzionare su OS X, a meno che non venga installato gnu-sed.


9
Non sono sicuro che funzioni in tutte le versioni di sed. Ho provato questo sul mio Mac e il \ n viene semplicemente emesso come 'n'
Todd Gamblin,

3
Ho trascorso 15 minuti sul Mac al mio lavoro, prima di leggere la tua risposta. Vai Apple!
Rick77,

1
Per coloro che usano l'homebrew: brew install gnu-sedseguito dagsed 's/regexp/\n&/g'
aaaaaa

1
... seguito daecho 'alias sed=gsed' >> ~/.bashrc
Proximo il

36

In sed, non è possibile aggiungere facilmente nuove righe nel flusso di output. Devi usare una linea di continuazione, che è imbarazzante, ma funziona:

$ sed 's/regexp/\
&/'

Esempio:

$ echo foo | sed 's/.*/\
&/'

foo

Vedi qui per i dettagli. Se vuoi qualcosa di leggermente meno imbarazzante potresti provare a usare perl -pecon gruppi di partite invece di sed:

$ echo foo | perl -pe 's/(.*)/\n$1/'

foo

$1 si riferisce al primo gruppo corrispondente nell'espressione regolare, in cui i gruppi sono tra parentesi.


Perché dici che non puoi aggiungere nuove righe? Puoi semplicemente fare sed 's / regexp / & \ n / g' Questo è tutto
Andres

2
Questa è la cosa meno bizzarra che puoi fare su un Mac per inserire newline (\ n non funziona su un mac)
Pylinux

La versione perl può essere modificata per effettuare modifiche in locoperl -pi -e 's/(.*)/\n$1/' foo
Eponymous

2
@Andres: (principalmente) le implementazioni Sed solo per le funzionalità POSIX come la versione BSD fornita anche con OS X non supportano le sequenze di escape dei caratteri di controllo nella parte di sostituzione di una schiamata di funzione (a differenza dell'implementazione GNU Sed, che lo fa) . La risposta sopra funziona con entrambe le implementazioni; per una panoramica di tutte le differenze, vedere qui .
mklement0

29

Sul mio mac, il seguente inserisce una singola 'n' invece di newline:

sed 's/regexp/\n&/g'

Questo sostituisce con newline:

sed "s/regexp/\\`echo -e '\n\r'`/g"

Stavo facendo la modifica in linea sed -i '' -e ...e stavo riscontrando problemi con la ^Mscrittura di un cursore M (ctrl + m) sul file. Ho finito per usare perl con gli stessi parametri.
Steve Tauber,

2
Si prega di essere consapevoli del fatto che il secondo codice inserisce uno speciale codice newline LF CR (retro del MS-DOS CR LF)! Sia i sistemi operativi Unix che Mac OS X usano solo LF ( \n).
pabouk,

Qualcos'altro nella mia espressione sed stava causando così tanta infelicità (nonostante funzionasse bene senza echo...e e newline) che l'ho fatto solo in modo vim.
Ahmed Fasih,

1
O semplicemente: sed "s/regexp/`echo`/g"- questo produrrà un singolo LF invece di LF-CR
mojuba

2
@mojuba: No: `echo`si tradurrà in una stringa vuota , perché le sostituzioni di comandi tagliano invariabilmente tutte le nuove righe finali. Non c'è modo di usare una sostituzione di comando per inserire direttamente una nuova riga (e inserire \n\r- cioè un CR aggiuntivo - è un'idea terribile).
mklement0

15
echo one,two,three | sed 's/,/\
/g'

1
+1 ha funzionato perfettamente e abbastanza avanti / facile da ricordare
gMale

2
Questa risposta è in realtà una soluzione sed piuttosto che una soluzione bash . Tutto ciò che utilizza costrutti come $'\n'si basa sulla shell per generare la nuova riga. Tali soluzioni potrebbero non essere portatili. Questo è. Naturalmente, è anche un duplicato del secondo esempio nella risposta di tgamblin del 2009.
ghoti,

10

In questo caso, non uso sed. Io uso tr.

cat Somefile |tr ',' '\012' 

Questo prende la virgola e la sostituisce con il ritorno a capo.


1
Ho scoperto che funziona anche: cat Somefile | tr ',' '\n'YMMV
LS

9

Puoi usare perl one-liner come fai con sed, con il vantaggio del pieno supporto dell'espressione regolare perl (che è molto più potente di quello che ottieni con sed). C'è anche una variazione molto piccola tra le piattaforme * nix - perl è generalmente perl. Quindi puoi smettere di preoccuparti di come far sì che la versione di sed del tuo sistema particolare faccia quello che vuoi.

In questo caso, puoi farlo

perl -pe 's/(regex)/\n$1/'

-pe mette il perl in un ciclo "esegui e stampa", proprio come la normale modalità operativa di sed.

' cita tutto il resto in modo che la shell non interferisca

()che circonda la regex c'è un operatore di raggruppamento. $1sul lato destro della sostituzione stampa tutto ciò che è stato abbinato all'interno di queste parentesi.

Infine, \nè una newline.

Indipendentemente dal fatto che tu stia usando le parentesi come operatore di raggruppamento, devi scappare da tutte le parentesi che stai cercando di abbinare. Quindi una regex per abbinare il modello che elenchi sopra sarebbe qualcosa di simile

\(\d\d\d\)\d\d\d-\d\d\d\d

\(o \)corrisponde a un valore letterale e \dcorrisponde a una cifra.

Meglio:

\(\d{3}\)\d{3}-\d{4}

Immagino che tu possa capire cosa stanno facendo i numeri tra parentesi graffe.

Inoltre, è possibile utilizzare delimitatori diversi da / per regex. Quindi, se devi abbinare / non dovrai scappare. Uno dei seguenti è equivalente alla regex all'inizio della mia risposta. In teoria puoi sostituire qualsiasi carattere con lo / gli standard.

perl -pe 's#(regex)#\n$1#'
perl -pe 's{(regex)}{\n$1}'

Un paio di pensieri finali.

usando -neinvece di -peatti allo stesso modo, ma non stampa automaticamente alla fine. Può essere utile se si desidera stampare da soli. Ad esempio, ecco un grep-alike ( m/foobar/è una partita regex):

perl -ne 'if (m/foobar/) {print}'

Se stai riscontrando problemi con le newline e vuoi che sia gestito magicamente per te, aggiungi -l. Non utile per l'OP, che stava lavorando con le nuove linee.

Suggerimento bonus: se hai installato il pacchetto pcre, viene fornito con pcregrepregex completamente compatibili perl.


4

Hmm, le righe appena sfuggite sembrano funzionare nelle versioni più recenti di sed(I have GNU sed 4.2.1),

dev:~/pg/services/places> echo 'foobar' | sed -r 's/(bar)/\n\1/;'
foo
bar

1
Come accennato, funziona con varie versioni di GNU sed, ma non con sed incluso in macOS.
LS

4
echo pattern | sed -E -e $'s/^(pattern)/\\\n\\1/'

ha funzionato bene su El Captitan con il ()supporto


Questo ha funzionato alla grande e hai anche dato un comando completo per testare ed estrapolare dalla specializzazione per il proprio scopo. Bel lavoro!
jxramos,

3

Per inserire una nuova riga nello stream di output su Linux, ho usato:

sed -i "s/def/abc\\\ndef/" file1

Dov'era file1:

def

Prima della sostituzione sul posto di sed e:

abc
def

Dopo la sostituzione sul posto di sed. Si prega di notare l'uso di \\\n. Se i motivi hanno un "interno, scappa usando \".


Per me il codice sopra non funziona. sedinserisce \ninvece di LF perché entra \\nnel parametro dalla shell. --- Questo codice funziona: sed -i "s/def/abc\ndef/" file1. --- GNU sed version 4.2.1, GNU bash, version 4.1.2(1) / 4.2.25(1)(CentOS versione 6.4 / Ubuntu 12.04.3).
pabouk,

2

in sed puoi fare riferimento a gruppi nel tuo pattern con "\ 1", "\ 2", .... quindi se il pattern che stai cercando è "PATTERN", e vuoi inserire "PRIMA" davanti ad esso , puoi usare, senza scappare

sed 's/(PATTERN)/BEFORE\1/g'

vale a dire

  sed 's/\(PATTERN\)/BEFORE\1/g'

Appena fatto: testfile contents = "ABC ABC ABC". Ho eseguito il file di test "sed 's / \ (ABC \) / \ n \ 1 / g', ottenuto le nuove righe. Sperimenta le fughe, prova ad aggiungere 1 cosa alla volta al tuo schema, ad esempio assicurati di abbinare il modello, quindi controlla la corrispondenza del gruppo, quindi aggiungi il controllo della nuova riga
Steve B.

Ho appena provato esattamente questo e ho ottenuto "nABC nABC nABC". Stai usando un'altra versione di sed?
Todd Gamblin,

la fuga della shell sta probabilmente ostacolando i tentativi di tgamblin. mettere tutti gli argomenti sed tra virgolette singole come ha fatto Steve B dovrebbe risolverlo. È possibile che diverse versioni di sed non comprendano \ n per newline.
Dan Pritts,

2

Puoi anche farlo con awk, usando -vper fornire lo schema:

awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file

Questo controlla se una linea contiene un determinato modello. In tal caso, aggiunge una nuova riga all'inizio di essa.

Vedi un esempio di base:

$ cat file
hello
this is some pattern and we are going ahead
bye!
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file
hello
this is some 
pattern and we are going ahead
bye!

Nota che influenzerà tutti i motivi in ​​una linea:

$ cat file
this pattern is some pattern and we are going ahead
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' d
this 
pattern is some 
pattern and we are going ahead

1
cosa fa l'1 in questo?
Whatahitson,

1
@whatahitson 1è usato in Awk come scorciatoia per {print $0}. Il motivo è che qualsiasi condizione che restituisce True attiva l'azione predefinita di Awk, che consiste nella stampa del record corrente.
fedorqui "SO smettere di danneggiare" il

1

Questo funziona in MAC per me

sed -i.bak -e 's/regex/xregex/g' input.txt sed -i.bak -e 's/qregex/\'$'\nregex/g' input.txt

Dono se è perfetto ...


1

Dopo aver letto tutte le risposte a questa domanda, mi ci sono voluti molti tentativi per ottenere la sintassi corretta al seguente script di esempio:

#!/bin/bash
# script: add_domain
# using fixed values instead of command line parameters $1, $2
# to show typical variable values in this example
ipaddr="127.0.0.1"
domain="example.com"
# no need to escape $ipaddr and $domain values if we use separate quotes.
sudo sed -i '$a \\n'"$ipaddr www.$domain $domain" /etc/hosts

Lo script aggiunge una nuova riga \nseguita da un'altra riga di testo alla fine di un file utilizzando un singolo sedcomando.


1
sed -e 's/regexp/\0\n/g'

\ 0 è il null, quindi la tua espressione viene sostituita con null (niente) e quindi ...
\ n è la nuova riga

Su alcune versioni di Unix non funziona, ma penso che sia la soluzione al tuo problema.

echo "Hello" | sed -e 's/Hello/\0\ntmow/g'
Hello
tmow

0

In vi su Red Hat, sono stato in grado di inserire i ritorni a capo usando solo il carattere \ r. Credo che questo esegua internamente "ex" anziché "sed", ma è simile e vi può essere un altro modo per eseguire modifiche collettive come patch di codice. Per esempio. Sto circondando un termine di ricerca con un'istruzione if che insiste sui ritorni a capo dopo le parentesi graffe:

:.,$s/\(my_function(.*)\)/if(!skip_option){\r\t\1\r\t}/

Nota che ho anche inserito alcune schede per allineare meglio le cose.

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.