Özyinelemeli bul ve komut satırından nasıl değiştirebilirim?


84

Bash veya zshell gibi bir kabuk kullanarak nasıl özyinelemeli 'bul ve değiştir' yapabilirim? Başka bir deyişle, bu dizindeki ve alt dizinlerindeki tüm dosyalardaki her 'foo' oluşumunu 'bar' ile değiştirmek istiyorum.


Aynı sorular için alternatif bir cevap burada bulunabilir stackoverflow.com/questions/9704020/…
dunxd


Bunu denemek için iyi bir fikir olabilir. Bu şekilde, istemediğiniz bir şeyi değiştirmediğinizden emin olmak için onay özelliğini kullanabilirsiniz. Dizinin geniş yapılıp yapılmadığından emin değilim.
Samy Bencherif

Yanıtlar:


106

Bu komut yapacak (hem Mac OS X Lion hem de Kubuntu Linux'ta test edilmiştir).

# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'

İşte nasıl çalışıyor:

  1. find . -type f -name '*.txt'Geçerli dizinde ( .) ve altında, -type fisimleri biten tüm düzenli dosyaları ( ) bulur ..txt
  2. | bu komutun çıktısını (bir dosya listesi) bir sonraki komuta geçirir
  3. xargs bu dosya adlarını toplar ve birer birer verir sed
  4. sed -i '' -e 's/foo/bar/g'"dosyayı yedekleme yerine yerinde düzenleme ve aşağıdaki değiştirme ( s/foo/bar) satır başına birden çok kez yapma ( /g)" anlamına gelir (bkz. man sed)

4. satırdaki 'yedekleme olmadan' bölümünün benim için uygun olduğunu unutmayın, çünkü değiştirdiğim dosyalar yine de sürüm kontrolü altında, bu nedenle bir hata olursa kolayca geri alabilirim.


9
Hiçbir zaman boru, -print0seçeneği olmadan xargs'e çıktı bulamaz . Komutunuz, adlarında boşluk vs. olan dosyalarda başarısız olacaktır.
slhck

20
Ayrıca, find -name '*.txt' -exec sed -i 's/foo/bar/g' {} +tüm bunları GNU bulgusu ile yapacağız.
Daniel Andersson

6
Ben olsun sed: can't read : No such file or directoryben çalıştırdığınızda find . -name '*.md' -print0 | xargs -0 sed -i '' -e 's/ä/ä/g', ancak find . -name '*.md' -print0birçok dosyaların bir listesini verir.
Martin Thoma

9
Ben arasındaki boşluğu kaldırırsanız, bu benim için çalışıyor -ive''
Kanadalı Luka

3
Rolün ''ardından sed -ineyin anlamı nedir ''?
Jas,

27
find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +

Bu xargsbağımlılığı ortadan kaldırır .


4
Bu GNU ile çalışmıyor sed, bu yüzden çoğu sistemde başarısız olacak. GNU ve sedarasında boşluk bırakmanızı gerektirmez . -i''
slhck

1
Kabul edilen cevaplar bunu açıklamak için daha iyi bir iş çıkarır. ancak doğru sözdizimini kullanmak için + 1.
Kasım’da

9

Git kullanıyorsanız, şunları yapabilirsiniz:

git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'

-lsadece dosya isimlerini listeler. -zHer sonuçtan sonra boş bir bayt yazdırır.

Bunu bitirdim çünkü bir projedeki bazı dosyaların dosyanın sonunda yeni bir satırı yoktu ve sed başka bir değişiklik yapmamış olsa bile yeni bir satır ekledi. (Dosyaların sonunda yeni bir satır olması gerekip gerekmediğine dair yorum yok. 🙂)


1
Bu çözüm için büyük +1. find ... -print0 | xargs -0 sed ...Çözümlerin geri kalanı sadece çok daha uzun sürmekle kalmıyor, aynı zamanda önceden bulunmayan dosyalara yeni satırlar ekliyor, bu da git repo içinde çalışırken sıkıntı yaratıyor. git grepkarşılaştırıldığında hızlı bir şekilde aydınlatıyor.
Josh Kupershmidt

3

İşte bunun için kullandığım benim zsh / perl işlevi:

change () {
        from=$1 
        shift
        to=$1 
        shift
        for file in $*
        do
                perl -i.bak -p -e "s{$from}{$to}g;" $file
                echo "Changing $from to $to in $file"
        done
}

Ve bunu kullanarak yürütürdüm

$ change foo bar **/*.java

(Örneğin)


2

Deneyin:

sed -i 's/foo/bar/g' $(find . -type f)

Ubuntu 12.04'te test edilmiştir.

Bu komut, eğer alt dizin isimleri ve / veya dosya isimleri boşluklar içeriyorsa işe yaramaz, fakat sizde varsa, bu komutu çalışmaz gibi kullanmazlar.

Dizin isimlerinde ve dosya isimlerinde boşluk kullanmak genellikle kötü bir uygulamadır.

http://linuxcommand.org/lc3_lts0020.php

"Dosya adları hakkında önemli bilgiler" konusuna bakın.


Adlarında boşluk bulunan dosya (lar) varken deneyin. (“Bir şey gerçek olamayacak kadar iyi görünüyorsa, muhtemelen öyledir” diyen bir kural vardır. ”3 yıl içinde başkalarının gönderdiği herhangi bir şeyden daha kompakt bir çözüm" keşfettiyseniz ", neden kendinize sormalısınız. bu olabilir.)
Scott

1

Bu Kabuk Betiğini Kullan

Şimdi, diğer cevaplardan ve web’deki aramalardan öğrendiğim şeyleri birleştiren bu kabuk betiğini kullanıyorum. Benim changeüzerinde bir klasörde adı verilen bir dosyaya yerleştirdim $PATHve yaptım chmod +x change.

#!/bin/bash
function err_echo {
  >&2 echo "$1"
}

function usage {
  err_echo "usage:"
  err_echo '  change old new foo.txt'
  err_echo '  change old new foo.txt *.html'
  err_echo '  change old new **\*.txt'
  exit 1
}

[ $# -eq 0 ] && err_echo "No args given" && usage

old_val=$1
shift
new_val=$1
shift
files=$* # the rest of the arguments

[ -z "$old_val" ]  && err_echo "No old value given" && usage
[ -z "$new_val" ]  && err_echo "No new value given" && usage
[ -z "$files" ]    && err_echo "No filenames given" && usage

for file in $files; do
  sed -i '' -e "s/$old_val/$new_val/g" $file
done

0

Benim kullanım durumu ben değiştirmek istediğini oldu foo:/Drive_Letterile foo:/bar/baz/xyz Benim durumumda Aşağıdaki kod ile bunu başardı. Aynı klasör konumunda, çok sayıda dosyanın bulunduğu yerdeydim.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

yardımcı oldu umarım.


0

Aşağıdaki komut Ubuntu ve CentOS'ta iyi çalıştı; ancak, OS XI kapsamında hatalar almaya devam etti:

find . -name Root -exec sed -i 's/1.2.3.4\/home/foo.com\/mnt/' {} \;

sed: 1: "./Root": geçersiz komut kodu.

Paraşütçeleri xargs aracılığıyla geçirmeyi denediğimde hatasız bir şekilde çalıştı:

find . -name Root -print0 | xargs -0 sed -i '' -e 's/1.2.3.4\/home/foo.com\/mnt/'

Eğer değişti gerçeği -iile -i ''muhtemelen değişmiş olmasından daha önemlidir -execiçin -print0 | xargs -0. Btw, muhtemelen ihtiyacınız yok -e.
Scott

0
# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'

Yukarıdaki bir cazibe gibi çalıştı, ancak bağlantılı dizinlerde, -Lona bayrak ekledim. Son versiyon şuna benziyor:

# Recursively find and replace in files
find -L . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.