Büyük bir klasör hiyerarşisinde metin değiştirme nasıl yapılır?


11

Aramak ve bazı örnekleri hariç büyük bir dosya kümesindeki bazı metni değiştirmek istiyorum. Her satır için, bu satırı değiştirmem gerekip gerekmediğini soran bir komut istemi istiyorum. Vim'lere benzer bir şey :%s/from/to/gc( conay isteminde ile), ancak bir dizi klasör arasında. Kullanılabilecek iyi bir komut satırı aracı veya komut dosyası var mı?


Doğru biçimlendirmenin önemi üzerine: Başlangıçta komutunuzu, yazmaya çalıştığınız şeye vurgu yapmak s/from/to/gyerine, bir biçimlendirme aksaklığında olduğu gibi okurdum (Markdown ile yapamazsınız, ve HTML etiketleri). s/from/to/gcc<code><strong>
Gilles 'SO- kötü olmayı bırak'

Yanıtlar:


19

Neden vim kullanmıyorsunuz?

Tüm dosyaları vim'de aç

vim $(find . -type f)

Veya yalnızca ilgili dosyaları açın (Caleb tarafından önerildiği gibi)

vim $(grep 'from' . -Rl)

Ve sonra değiştirmeyi tüm arabelleklerde çalıştırın

:bufdo %s/from/to/gc | update

Bunu da yapabilirsiniz sedama benim sed bilgim sınırlıdır.


Teşekkürler, cevabınız geç bir çift çekmemi sağladı: Etkileşimli biti tamamen özlediğimi fark ettim. Sed ile bile bunun mümkün olduğunu düşünmüyorum (yeterli giriş / çıkış kanalı yok).
Gilles 'SO- kötü olmayı bırak'

1
Bunu grepyerine findyalnızca geçerli eşleşmeleri olan dosyaları açmak için kullanarak geçerli arabellekteki TÜM dosyaları açmayarak bunu hızlandırabilirsiniz . vim $(grep 'from' . -Rl)
Caleb

Thanks.The c (c etrafında astreriks) ihtiyaç vardır? ya da bir biçimlendirme sorunu mu?
balki

@balki "biçimlendirme" sorunu. Düzeltildi
Gert

5

-l -peArgüman ( -i) olarak iletilen dosyalar üzerinde yerinde satır satır ( ) değiştirmeleri talimatı verilen küçük bir Perl betiği ile kaba bir şey yapabilirsiniz :

perl -i -l -pe '
    if (/from/) {                            # is the source text present on this line?
        printf STDERR ("%s: %s [y/N]? ", $ARGV, $_);  # display a prompt
        $r=<STDIN>;                                   # read user response
        if ($r =~ /^[Yy]/) {                          # if user entered Y:
            s/from/to/g;                              # replace all occurences on this line
    }' /path/to/files

Olası iyileştirmeler istemin bölümlerini renklendirmek ve “geçerli dosyadaki tüm olayları değiştir” gibi şeyleri desteklemek olabilir. Bir satırdaki her bir olayı ayrı ayrı istemek daha zor olurdu.

İkinci bölüm, dosyaları eşleştirme. çok fazla dosya yoksa ve zsh çalıştırıyorsanız, geçerli dizindeki ve alt dizinlerindeki tüm dosyaları özyinelemeli olarak eşleştirebilirsiniz:

perl -i -l -pe '…' **/*(.)

Kabuğunuz bash ≥4 ise, çalıştırabilirsiniz perl … **/*, ancak sed dizinlerde çalışmayı dener (ve başarısız olur) çünkü sahte hata iletileri üretir. Değiştirme işlemini yalnızca C dosyaları gibi bir dizi dosyada gerçekleştirmek istiyorsanız, eşleşmeleri kısıtlayabilirsiniz (bash ≥4 veya zsh biçiminde çalışır):

perl -i -l -pe '…' **/*.[hc]

Hangi dosyaları değiştirdiğiniz üzerinde daha iyi denetime ihtiyacınız varsa veya kabuğunuzda özyinelemeli dizin eşleştirme yapısı **yoksa veya çok fazla dosyanız varsa ve “komut satırı çok uzun” hatası alıyorsanız kullanın find. Örneğin , geçerli dizinde *.hveya *.calt dizinlerinde bulunan tüm dosyalarda ve alt dizinlerinde bir değiştirme gerçekleştirmek için (eski sistemlerde, satırın sonunda \;yerine kullanmanız gerekebilir +( +form daha hızlıdır, ancak her yerde kullanılabilir değildir).

find . -type f -name '*.[hc]' -exec perl -i -l -pe '…' {} +

Bununla birlikte, etkileşime ihtiyacınız varsa etkileşimli bir editöre bağlı kalırım. Gert, Vim'de buna bir yol gösterdi , ancak aramak istediğiniz tüm dosyaları açmayı gerektirir, ancak çok fazla varsa sorun olabilir.

Emacs'ta bunu nasıl yapabileceğiniz aşağıda açıklanmıştır:

  1. Dosya adlarını M-x find-name-dired(bir üst düzey dizin M-x find-diredbelirtin ) veya (rasgele bir findkomut satırı belirtin ) ile toplayın .
  2. Ortaya çıkan dired arabelleğinde, ttüm dosyaları işaretlemek için düğmesine basın , ardından işaretli dosyalar üzerinde bilgi istemiyle bir değiştirme gerçekleştirmek için Q( dired-do-query-replace-regexp) düğmesine basın .

1

sdiff(bkz. http://www.gnu.org/software/diffutils/manual/diffutils.html#Invoking-sdiff ) burada kullanışlı olabilir. Bununla beraber etkileşimli yama yapabilirsiniz. Bu nedenle, değiştirme işlemleri yaparak oluşturduğunuz geçici bir dosyayla yapmak sedolası bir çözüm olabilir:

# use file descriptor 3 to still allow use of stdin
while IFS= read -r -d '' file <&3; do

  # write the result of the replacement into a temporary file
  sed -r 's/something/something_else/g' -- "$file" > replacer_tmp

  if cmp -s -- "$file" replacer_tmp; then
    continue; # nothing was replaced
  fi

  echo "There is something to replace in '$file'! Starting interactive diff."
  echo

  sdiff -o "$file" -s -d -- "$file" replacer_tmp

  echo

done 3< <(find . -type f -print0)

(POSIX olmayan işlem ikamesi kullanan ve read -dörn. Tarafından desteklenen dosya döngüsü bash.)

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.