Come segnalare i cambiamenti sul posto "sed"


23

Quando si utilizza sedper sostituire le stringhe sul posto, esiste un modo per riportare le modifiche apportate (senza fare affidamento su una diff di file vecchi e nuovi)?

Ad esempio, come posso modificare la riga di comando

find . -type f | xargs sed -i 's/abc/def/g'

così posso vedere le modifiche apportate al volo?

Risposte:


15

Si potrebbe utilizzare sed's wcontrassegna con entrambi /dev/stderr, /dev/tty, /dev/fd/2se supportato sul sistema. Ad esempio con un input filecome:

foo first
second: missing
third: foo
none here

in esecuzione

sed -i '/foo/{
s//bar/g
w /dev/stdout
}' file

uscite:

bar first
third: bar

sebbene il filecontenuto sia stato cambiato in:

bar first
second: missing
third: bar
none here

Quindi, nel tuo caso, esegui:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
s//bar/g
w /dev/fd/2
}' {} \;

modificherà i file sul posto e produrrà:

./file1:
roba da bar
più bar

./file2:

./file3:
prima il bar
terzo: bar

Puoi anche stampare qualcosa come original line >>> modified linead esempio:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
h
s//bar/g
H
x
s/\n/ >>> /
w /dev/fd/2
x
}' {} \;

modifica i file sul posto e genera:

./file1:
roba da foo >>> roba da bar
più foo >>> più barra

./file2:

./file3:
prima >>> prima la barra
terzo: pippo >>> terzo: bar

16

Potresti farlo in due passaggi usando l' pazione rint al primo passaggio con:

find . -type f | xargs sed --quiet 's/abc/def/gp'

dove --quietrende sed non mostrare tutte le linee e il psuffisso mostra solo le linee in cui la sostituzione è stata abbinata.

Ciò ha la limitazione che sed non mostrerà quali file vengono modificati e che ovviamente potrebbero essere corretti con una certa complessità aggiuntiva.


Non fa esattamente quello che volevo dal momento che hai bisogno di due passaggi, ma sto votando perché ho appena imparato questa cosa p ed è molto utile. Soprattutto se, come hai detto, sono stati stampati anche i file. Qualcosa di similefor x in `find . -type f`; do echo ///File $x: ; sed --quiet 's/abc/def/gp' $x; done
ricab,

@msw Ho molte sedespressioni, non voglio scrivere /gpper ognuna. Come impostarlo a livello globale?
alhelal

8

Non credo sia possibile, ma una soluzione alternativa potrebbe essere quella di utilizzare perl invece:

find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 

Ciò stamperà le righe modificate con errori standard. Per esempio:

$ cat foo
fooabcbar
$ find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 
foodefbar

Puoi anche renderlo leggermente più complesso, stampando il numero di riga, il nome del file, la riga originale e la riga modificata:

$ find . -type f | 
   xargs perl -i -ne '$was=$_; chomp($was);
                      s/abc/def/ && print STDERR "$ARGV($.): $was : $_"' 
./foo(1): fooabcbar : foodefbar

+1 È inoltre possibile utilizzare $ARGVper il nome del file su cui si opera.
Joseph R.,

@JosephR. buon punto, aggiunto.
terdon

Grazie, probabilmente è utile (non l'ho provato da solo). Non sto selezionando in quanto non utilizza sed, quindi non risponde esattamente alla domanda.
ricab,

@ricab va bene, non si dovrebbe accettare una risposta a meno che in realtà le risposte alla tua domanda. Ricorda però che per cose semplici come questa, perlla sintassi è molto simile a quella sede non credo che ciò che stai chiedendo sia effettivamente possibile sed.
terdon

3

È possibile usare il flag w che scrive il modello corrente su un file. Quindi aggiungendolo al comando sostitutivo possiamo riportare le sostituzioni successive in un file e stamparlo dopo che il lavoro è terminato. Mi piace anche colorare la stringa sostituita con grep.

sed -i -e "s/From/To/gw /tmp/sed.done" file_name
grep --color -e "To" /tmp/sed.done

Si noti che deve esserci solo uno spazio tra w e il suo nome file.

Questo è persino meglio di diff, poiché diff potrebbe anche mostrare cambiamenti anche se non sono stati fatti da sed.


L'ho appena testato e sed scrive solo righe a cui ha sostituito sed.done. Le linee con "A" nel file originale non vengono quindi stampate su sed.done, quindi quando grep "To" sed.donevedrai solo le linee che vengono modificate da sed. Non vedrai la riga originale nel file prima che fosse sostituita, se è quello a cui stai mirando ...
aairey,

1

Mi piace la soluzione @terdon - perl è buono per questo.

Ecco la mia versione ottimizzata che:

  • non tenterà di modificare i file che non hanno la stringa corrispondente
  • eseguirà il backup della versione precedente dei file che cambiano (crea una versione .bak)
  • elencherà ogni file / lineno modificato e mostrerà le VECCHIE e NUOVE versioni di quella riga rientrata e sottostante per una facile lettura

codice

find /tmp/test -type f ! -name "*.bak" -exec grep -l '/opt/gridmon' {} \; | xargs -L1 perl -ni'.bak' -e'$old=$_; s/\/opt\/gridmon/~/g && print STDERR "$ARGV($.):\n\tOLD:$old\tNEW:$_"'

esempio di output

/tmp/test/test4.cfg(13):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
/tmp/test/test4.cfg(24):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
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.