2009-05-25 8 views
53

Potrzebuję wykonać wyrażenie regularne i zamienić na wszystkie pliki w folderze (i jego podfolderach). Jakie byłoby polecenie powłoki linuxa, aby to zrobić?sed beginner: zmiana wszystkich wystąpień w folderze

Na przykład, chcę uruchomić to nad wszystkimi plikami i zastąpić stary plik nowym, zastąpionym tekstem.

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

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

Odpowiedz

82

Nie ma sposobu, aby to zrobić tylko przy użyciu sed. Będziesz musiał użyć przynajmniej narzędzia find razem:

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

To polecenie utworzy plik .bak dla każdego pliku zmieniło.

Uwagi:

  • -i argumentem dla komendy sed to rozszerzenie GNU, więc, jeśli używasz tego polecenia z BSD sed trzeba będzie przekierować wyjście do nowego pliku, a następnie zmienić nazwę to.
  • Narzędzie find nie implementuje argumentu -exec w starych skrzynach UNIX, więc zamiast tego należy użyć | xargs.
0

Czy mogę zasugerować (po wykonaniu kopii zapasowej plików):

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

Może chcesz spróbować my mass search/replace Perl script. Ma pewne zalety w porównaniu z rozwiązaniami łańcuchowo-użytkowymi (takimi jak brak konieczności radzenia sobie z wieloma poziomami interpretacji metaznaków powłoki).

5

Dla przenośności, nie polegam na funkcjach sed, które są specyficzne dla Linuksa lub BSD. Zamiast tego używam skryptu overwrite z książki Kernighana i Pike'a z Unix Programming Environment.

Polecenie jest wtedy

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

A overwrite skrypt (którego używam wszędzie) jest

#!/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 

Chodzi o to, że nadpisuje plik tylko wtedy, gdy polecenie powiedzie. Przydatne w find a także wtedy, gdy nie chcesz używać

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

ponieważ powłoka obcina plik przed sed można go odczytać.

20

Preferuję używać find | xargs cmd przez find -exec, ponieważ łatwiej jest zapamiętać.

Ten przykład globalnie zastępuje „foo” z „bar” w plikach .txt na lub poniżej bieżącego katalogu:

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

W -print0 i -0 opcje mogą być pominięte, jeśli nazwy plików nie zawierają znaki funkowe takie jak spacje.

+1

Jeśli jesteś na OSX, spróbuj 'znaleźć. -type f -name "* .txt" -print0 | xargs -0 sed -i '' "s/foo/bar/g" '(uwaga podająca pusty ciąg do argumentu" -i "). – jkukul

-4

Jeśli nazwa pliku w folderze ma pewne zwykłe nazwy (jak plik1, plik2 ...) użyłem do cyklu.

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

nie jest to związane z zadawanym pytaniem. Pytanie nie wspomina nic o tym samym wzorze nazwy pliku/folderu. Proszę unikać takich odpowiedzi –