2010-04-06 4 views
17

Sto usando sed in uno script per fare una sostituzione e voglio che il file sostituito sovrascriva il file. Normalmente penso che usereste questo:Reindirizzare l'output da sed 's/c/d /' myFile a myFile

% sed -i 's/cat/dog/' manipulate 
sed: illegal option -- i 

Tuttavia come potete vedere il mio sed non ha questo comando.

ho provato questo:

% sed 's/cat/dog/' manipulate > manipulate 

Ma questo si trasforma solo manipolare in un file vuoto (ha un senso).

Questo funziona:

% sed 's/cat/dog/' manipulate > tmp; mv tmp manipulate 

ma mi chiedevo se ci fosse un modo standard per reindirizzare l'uscita nello stesso file di input che è stato preso da.

+2

Come mai nessuno ha menzionato "spugna"? –

+2

Immagino che la domanda non fosse [spugnosa] (http://en.wikipedia.org/wiki/The_Sponge). – ixe013

risposta

35

ho comunemente utilizzare il terzo modo, ma con un importante cambiamento :

$ sed 's/cat/dog/' manipulate > tmp && mv tmp manipulate

Vale a dire cambiare ; a && quindi lo spostamento si verifica solo se sed ha esito positivo; altrimenti perderai il tuo file originale non appena avrai fatto un refuso nella tua sintassi sed.

Nota! Per coloro che leggendo il titolo e manca vincolo del PO "il mio sed non supporta -i": Per la maggior parte delle persone, sed sosterrà -i, quindi il modo migliore per farlo è:

$ sed -i 's/cat/dog/' manipulate

1

Forse -i è gnu sed, o solo una vecchia versione di sed, ma comunque. Sei sulla strada giusta. La prima opzione è probabilmente la più comune, la terza opzione è se si desidera che funzioni ovunque (incluse le macchine solaris) ... :) Questi sono i modi "standard" per farlo.

+1

Sì, -i è specifico per gnu. – amertune

+0

'-i' è anche supportato in FreeBSD/MacOSX' sed' – Isaac

0

L'opzione -i non è disponibile nello standard sed.

Le alternative sono la terza via o perl.

3

Kernighan e Pike in La programmazione Art of Unix discute questo problema. La loro soluzione è scrivere uno script chiamato overwrite, che consente di fare queste cose.

L'utilizzo è: overwritefilecmdfile.

# overwrite: copy standard input to output after EOF 

opath=$PATH 
PATH=/bin:/usr/bin 

case $# in 
0|1) echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2 
esac 

file=$1; shift 
new=/tmp/overwr1.$$; old=/tmp/overwr2.$$ 
trap 'rm -f $new $old; exit 1' 1 2 15 # clean up 

if PATH=$opath "[email protected]" >$new 
then 
     cp $file $old   # save original 
     trap '' 1 2 15   # wr are commmitted 
     cp $new $file 
else 
     echo "overwrite: $1 failed, $file unchanged" 1>&2 
     exit 1 
fi 
rm -f $new $old 

Una volta che avete lo script precedente nel vostro $PATH, si può fare:

overwrite manipulate sed 's/cat/dog/' manipulate 

Per rendere la vita più facile, è possibile utilizzare replace scritto dallo stesso libro:

# replace: replace str1 in files with str2 in place 
PATH=/bin:/usr/bin 

case $# in 
    0|2) echo 'Usage: replace str1 str2 files' 1>&2; exit 1 
esac 

left="$1"; right="$2"; shift; shift 

for i 
do 
    overwrite $i sed "[email protected][email protected][email protected]" $i 
done 

Avere replace nel tuo $PATH ti consentirà inoltre di dire:

replace cat dog manipulate 
+1

Oppure in questo caso è sufficiente scaricare una nuova versione di sed, compatibile con l'opzione -i, da www.sunfreeware.com;) – Anders

+0

+1 Roba buona! Rubando questi due immediatamente ... –

+0

+1, lo script di sovrascrittura è molto utile (dopo alcune modifiche in base alle preferenze personali)! – Arkku

-2

Soluzione utilizzando handle di file aperti: file aperto

exec 3<manipulate 

impediscono di essere troncato:

rm manipulate 
sed 's/cat/dog/' <&3 > manipulate 
+1

Mentre tecnicamente corretto e un trucco carino, questo è ** pericoloso **. Se la tua energia si interrompe poco dopo l'esecuzione di 'sed', finirai con i dati mancanti. – Gilles

+0

Collega semplicemente altrove prima di rimuovere e andrà bene. –

6

Se non si desidera spostare copie in tutto, si potrebbe usare ndr:

ed file.txt <<EOF 
%s/cat/dog/ 
wq 
EOF 
+0

supporta tutte le espressioni regolari che sed fa? In particolare intervalli? – sixtyfootersdude

+0

ed supporta tutte le espressioni regolari che sed fa, compresi gli intervalli. edita su file sed - funziona su flussi L'altra differenza principale è che un comando ed, per impostazione predefinita funzionerà solo sulla riga corrente di un file (che è il motivo per cui ho modificato il mio esempio e aggiunto il%, per far eseguire il comando su tutte le linee), mentre i comandi sed, per impostazione predefinita, vengono eseguiti su tutte le linee. – amertune

+0

puoi anche usare "ex", che è l'alias di "vim". È molto ricco di funzionalità. – romaninsh

8

Sì, -i è supportato anche in FreeBSD/MacOS X sed, ma ha bisogno della stringa vuota come argomento per modificare un file sul posto.

sed -i "" 's/old/new/g' file # FreeBSD sed 
+0

'-i' non ha bisogno di una stringa vuota. Il valore del parametro è l'estensione di backup da utilizzare, quando è una stringa vuota: non verrà eseguito alcun backup. Su GNU sed il valore predefinito per '-i' è una stringa vuota. – bartimar

+1

È il mio risparmiatore di vita –

2

È possibile utilizzare sponge dal moreutils.

sed "s/cat/dog/" manipulate | sponge manipulate 
1

Per modificare più file (e il salvataggio di un backup di ciascuno come * .bak):

perl -p -i -e "s/oldtext/newText/g" *

replaces any occurence of oldtext by newtext in all files in the current folder. However you will have to escape all perl special characters within oldtext and newtext using the backslash 

This is called a “Perl pie” (mnemonic: easy as a pie) 
The -i flag tells it do do in-place replacement, and it should be ok to use single (“'”) as well as double (“””) quotes. 

    If using ./* instead of just *, you should be able to do it in all sub-directories 
See man perlrun for more details, including how to take a backup file of the original. 
using sed: 
      sed -i 's/old/new/g' ./* (used in GNU) 
    sed -i '' 's/old/new/g' ./* (used in FreeBSD) 
0

Molte risposte, ma nessuna è corretta. Qui è il più corretto e semplice:

$ echo "111 222 333" > file.txt 
$ sed -i -s s/222/444/ file.txt 
$ cat file.txt 
111 444 333 
$ 
+1

L'OP ha chiaramente affermato che il flag '-i' è considerato" illegale "dalla sua versione di' sed'. Quindi non può essere usato ... – arkascha

+1

leggere le domande prima di rispondere è sicuramente una virtù – Yerken