2009-05-25 6 views
53

Ho bisogno di fare una regex di trovare e sostituire su tutti i file in una cartella (e le sue sottocartelle). Quale sarebbe il comando della shell linux per farlo?sed principiante: modifica di tutte le occorrenze in una cartella

Ad esempio, voglio eseguire questo su tutti i file e sovrascrivere il vecchio file con il nuovo testo sostituito.

sed 's/old text/new text/g' 
+0

http://theunixshell.blogspot.com/2012/12/find-and-replace-string-in-all-files.html – Vijay

risposta

82

Non v'è alcun modo per farlo utilizzando sed solo. Avrete bisogno di utilizzare almeno l'utilità di trovare insieme:

find . -type f -exec sed -i.bak "s/foo/bar/g" {} \; 

Questo comando creerà un file .bak per ogni file modificato.

Note:

  • L'argomento -i per sed comando è un'estensione GNU, quindi, se si esegue questo comando con il BSD del sed è necessario reindirizzare l'output in un nuovo file rinominare esso.
  • L'utilità find non implementa l'argomento -exec nelle vecchie finestre UNIX, quindi, sarà necessario utilizzare un | xargs.
0

potrei suggerire (dopo il backup dei file):

find /the/folder -type f -exec sed -ibak 's/old/new/g' {} ';' 
-3

Potrebbe voler provare my mass search/replace Perl script. Ha alcuni vantaggi rispetto alle soluzioni di utilità concatenate (come non dover affrontare più livelli dell'interpretazione del metacharacter della shell).

5

Per la portabilità, non mi affido alle funzionalità di sed specifiche per linux o BSD. Invece io uso lo script overwrite da Kernighan e il libro di Pike sull'ambiente di programmazione Unix.

Il comando è quindi

find /the/folder -type f -exec overwrite '{}' sed 's/old/new/g' {} ';' 

e lo script overwrite (che io uso in tutto il luogo) è

#!/bin/sh 
# overwrite: copy standard input to output after EOF 
# (final version) 

# set -x 

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

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

if "[email protected]" >$new    # collect input 
then 
    cp $file $old # save original file 
    trap 'trap "" 1 2 15; cp $old $file  # ignore signals 
      rm -f $new $old; exit 1' 1 2 15 # during restore 
    cp $new $file 
else 
    echo "overwrite: $1 failed, $file unchanged" 1>&2 
    exit 1 
fi 
rm -f $new $old 

L'idea è che si sovrascrive un file solo se un comando ha esito positivo. Utile in find e anche dove non si vuole utilizzare

sed 's/old/new/g' file > file # THIS CODE DOES NOT WORK 

perché la shell tronca il file prima di sed grado di leggerlo.

20

Preferisco usare find | xargs cmd su find -exec perché è più facile da ricordare.

Questo esempio sostituisce a livello globale "pippo" con "bar" in file .txt o al di sotto la directory corrente:

find . -type f -name "*.txt" -print0 | xargs -0 sed -i "s/foo/bar/g" 

I -print0 e -0 opzioni possono essere omessi se i nomi dei file non contengono caratteri funky come spazi

+1

Se siete su OSX, prova a 'find. -type f -name "* .txt" -print0 | xargs -0 sed -i '' "s/foo/bar/g" '(nota fornendo una stringa vuota all'argomento' -i'). – jkukul

-4

Nel caso in cui il nome dei file nella cartella abbia alcuni nomi regolari (come file1, file2 ...) ho usato per il ciclo.

for i in {1..10000..100}; do sed 'old\new\g' 'file'$i.xml > 'cfile'$i.xml; done 
+0

questo non è correlato alla domanda posta. La domanda non menziona nulla sullo stesso modello di nome file/cartella. Si prega di evitare tali risposte –