Birden fazla dosyayı değiştirme


194

Aşağıdaki komut 2 dosyanın içeriğini doğru bir şekilde değiştirmektedir.

sed -i 's/abc/xyz/g' xaa1 xab1 

Ama yapmam gereken bu tür dosyaları dinamik olarak değiştirmek ve dosya adlarını bilmiyorum. İle başlayan xa*ve seddosya içeriğini değiştirmek gerekir geçerli dizindeki tüm dosyaları okuyacak bir komut yazmak istiyorum .


63
Yani sed -i 's/abc/xyz/g' xa*?
— Paul R

3
Buradaki cevaplar yeterli değil. Bkz. Unix.stackexchange.com/questions/112023/…
— Isaac

Yanıtlar:


136

Daha iyisi:

for i in xa*; do
    sed -i 's/asd/dfg/g' $i
done

çünkü kimse kaç dosya olduğunu bilmiyor ve komut satırı sınırlarını aşmak kolay.

Çok fazla dosya olduğunda şunlar olur:

# grep -c aaa *
-bash: /bin/grep: Argument list too long
# for i in *; do grep -c aaa $i; done
0
... (output skipped)
#

18
Bu kadar çok dosya varsa, komutta komut satırı sınırını forkıracaksınız. Kendinizi bundan korumak için kullanmak zorundasınızfind ... | xargs ...
— Glenn Jackson

1
Ben uygulama bilmiyorum, ama "xa *" desen bir noktada genişletilmesi gerekiyor. Kabuk, genişlemeyi forolduğundan daha farklı yapıyor mu echoveya grep?
— glenn jackman

4
güncellenmiş cevaba bakınız. Daha fazla bilgiye ihtiyacınız varsa, lütfen resmi bir soru sorun, böylece insanlar size yardımcı olabilir.
— lenik

5
Sed komutu, kullanmak gerekir "$i"yerine $iboşluklarla dosya adları üzerinde önlemek kelime yarılmasına. Aksi takdirde bu çok güzel.
— Joker

4
Liste ile ilgili olarak, farkın, forsadece bir yerleşik bile değil, dil sözdiziminin bir parçası olduğuna inanıyorum . Çünkü sed -i 's/old/new' *, genişletilmesi *TÜMÜNÜ sed için bir argüman olarak geçirilmeli ve sedişlemin başlatılmadan önce bunun olması gerektiğinden oldukça eminim . forDöngüyü kullanarak, tam arglist (genişlemesi *) hiçbir zaman komut olarak geçirilmez, yalnızca kabuk belleğinde saklanır ve yinelenir. Bununla ilgili herhangi bir referansım yok, sadece fark var gibi görünüyor. (Daha bilgili birinden duymak isterim ...)
— Wildcard

167

Kimse bulmak için -exec argüman bahsetmedi şaşırdım, bu tür bir kullanım-case için tasarlanmıştır, ancak her eşleşen dosya adı için bir işlem başlatacak:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} \;

Alternatif olarak, daha az işlemi çağıracak xargs kullanılabilir:

find . -type f -name 'xa*' | xargs sed -i 's/asd/dsg/g'

Ya da daha çok bulmanın alt işlem çağrısı başına birden fazla dosya sağlamasına izin vermek için find yerine + exec değişkenini kullanın ;:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +

8
Bu cevaptaki komutu bu şekilde değiştirmek zorunda kaldım: find ./ -type f -name 'xa*' -exec sed -i '' 's/asd/dsg/g' {} \;bu, find komutunun konumu ./ve -iOSX için tek bir çift tırnak .
— shelbydz

Find komutu ealfonso tarafından sağlandığı gibi çalışır , sadece backupsuffix parametresine ./eşittir .ve sonradır -i.
— uhausbrand

Bulma -execseçeneği {} +, sorunu belirtildiği gibi çözmek için yeterlidir ve çoğu gereksinim için iyi olmalıdır. Ancak xargsgenel olarak daha iyi bir seçimdir çünkü seçenekle paralel işlemeye de izin verir -p. Glob genişlemeniz komut satırı uzunluğunuzu aşacak kadar büyük olduğunda, sıralı bir çalıştırmada bir hızlandırmadan da yararlanabilirsiniz.
— Amit Naidu

78

Birlikte grep ve sed kullanabilirsiniz. Bu, alt dizinleri özyinelemeli olarak aramanızı sağlar.

Linux: grep -r -l <old> * | xargs sed -i 's/<old>/<new>/g'
OS X: grep -r -l <old> * | xargs sed -i '' 's/<old>/<new>/g'

For grep:
    -r recursively searches subdirectories 
    -l prints file names that contain matches
For sed:
    -i extension (Note: An argument needs to be provided on OS X)

3
Benim için bu yöntemin bonusu ben grep -vgit klasörleri önlemek için bir kayma oldugrep -rl <old> . | grep -v \.git | xargs sed -i 's/<old>/<new>/g'
— Martin Lyne

Mac için en iyi çözüm!
— Marquis Blount

30

Bu komutlar sedMac OS X ile birlikte gelen varsayılan ayarlarda çalışmaz .

Gönderen man 1 sed:

-i extension
             Edit files in-place, saving backups with the specified
             extension.  If a zero-length extension is given, no backup 
             will be saved.  It is not recommended to give a zero-length
             extension when in-place editing files, as you risk corruption
             or partial content in situations where disk space is exhausted, etc.

Denenmiş

sed -i '.bak' 's/old/new/g' logfile*

ve

for i in logfile*; do sed -i '.bak' 's/old/new/g' $i; done

Her ikisi de iyi çalışıyor.


2
@Sumek OS X'te tüm olayların yerini alan sed'i gösteren örnek bir terminal oturumu: GitHub Gist
— funroll

Bunu tüm web sitesi yapılandırma dosyalarımdaki iki farklı satırı aşağıdaki bir astarla değiştirmek için kullandım. sed -i.bak "s / supercache_proxy_config / proxy_includes \ / supercache_config / g; s / basic_proxy_config / proxy_include \ / basic_proxy_config / g" sites-available / * Dosya için işiniz bittiğinde * .bak dosyalarını silmeyi unutmayın sistem hijyeni uğruna.
— Josiah

19

@PaulR bunu bir yorum olarak gönderdi, ancak insanlar bunu bir cevap olarak görmelidir (ve bu cevap ihtiyaçlarım için en iyi şekilde çalışır):

sed -i 's/abc/xyz/g' xa*

Bu, muhtemelen onlarca sırada, ancak milyonlarca sırada değil, ılımlı miktarda dosya için çalışacaktır .


Değiştirmelerinizde eğik çizgiler olduğunu varsayın. Dosyayollarıyla başka bir örnek sed -i 's|auth-user-pass nordvpn.txt|auth-user-pass /etc/openvpn/nordvpn.txt|g' *.ovpn.
— Léo Léopold Hertz 준영

10

Bir başka çok yönlü yol da kullanmaktır find:

sed -i 's/asd/dsg/g' $(find . -type f -name 'xa*')

1
bu find komutunun çıktısı genişletilir, bu nedenle bu sorunu gidermez. Bunun yerine
— -exec

@erjoalgo bu, sed komutunun birden çok girdi dosyasını işleyebilmesi nedeniyle çalışır. Find komutunun genişletilmesi tam olarak çalışması için gerekliydi.
— dkinzer

dosya sayısı komut satırı sınırlarına girmediği sürece çalışır.
— ealfonso

Bu sınır yalnızca makinenin kullanabileceği bellek kaynaklarına bağlıdır ve yürütme sınırıyla tamamen aynıdır.
— dkinzer

4
Bu doğru değil. Yukarıdaki komutunuzda, $ (find. ...) tek bir komutta genişletilir ve eşleşen birçok dosya varsa çok uzun olabilir. Çok uzunsa (örneğin sistemimde sınır 2097152 karakter civarındaysa) bir hata alabilirsiniz: "Bağımsız değişken listesi çok uzun" ve komut başarısız olur. Bu konuda biraz bilgi almak için lütfen bu hatayı google.
— ealfonso

2

findBenzer bir görev için kullanıyorum . Oldukça basit: bunun için bir argüman olarak geçmelisiniz sed:

sed -i 's/EXPRESSION/REPLACEMENT/g' `find -name "FILE.REGEX"`

Bu şekilde karmaşık döngüler yazmak zorunda kalmazsınız ve hangi dosyaları değiştireceğinizi görmek, çalıştırmadan findönce çalıştırmak kolaydır sed.


1
Bu @ dkinzer'in cevabı ile tamamen aynı .
— Bay Tao

0

yapabilirsin

' xxxx ' metni arar ve ' yyyy ' ile değiştirir

grep -Rn '**xxxx**' /path | awk -F: '{print $1}' | xargs sed -i 's/**xxxx**/**yyyy**/'

0

Bir komut dosyası çalıştırabiliyorsanız, benzer bir durum için yaptığım şey:

Bir sözlük / hashMap (ilişkilendirilebilir dizi) ve sedkomut için değişkenler kullanarak, birkaç dizeyi değiştirmek için dizi arasında döngü yapabiliriz. Joker karakter name_patterneklenmesi name_pattern='File*.txt', belirli bir dizindeki ( source_dir) bir desenle (bu gibi bir şey olabilir ) dosyalarda yerinde değiştirmeye izin verecektir . Tüm değişiklikler yazılır logfileiçindedestin_dir

#!/bin/bash
source_dir=source_path
destin_dir=destin_path
logfile='sedOutput.txt'
name_pattern='File.txt'

echo "--Begin $(date)--" | tee -a $destin_dir/$logfile
echo "Source_DIR=$source_dir destin_DIR=$destin_dir "

declare -A pairs=( 
    ['WHAT1']='FOR1'
    ['OTHER_string_to replace']='string replaced'
)

for i in "${!pairs[@]}"; do
    j=${pairs[$i]}
    echo "[$i]=$j"
    replace_what=$i
    replace_for=$j
    echo " "
    echo "Replace: $replace_what for: $replace_for"
    find $source_dir -name $name_pattern | xargs sed -i "s/$replace_what/$replace_for/g" 
    find $source_dir -name $name_pattern | xargs -I{} grep -n "$replace_for" {} /dev/null | tee -a $destin_dir/$logfile
done

echo " "
echo "----End $(date)---" | tee -a $destin_dir/$logfile

Birincisi, çiftleri dizisi bildirildi, her bir çift daha sonra, bir yedek dizesi WHAT1için değiştirilecek FOR1ve OTHER_string_to replaceiçin değiştirilecektir string replaceddosyada File.txt. Döngüde dizi okunur, çiftin ilk üyesi olarak replace_what=$ive ikincisi olarak alınır replace_for=$j. findDizinde komut arar dosya adı (yani bir joker içerebilir) ve sed -idaha önce tanımlanan ne aynı dosya (lar) komut cümledeki. Sonunda birgrep , dosyalarda yapılan değişiklikleri günlüğe kaydetmek için günlük dosyasına yeniden yönlendirme .

Bu benim için çalıştı GNU Bash 4.3 sed 4.2.2ve VasyaNovikov'un bash'daki tuples üzerine Döngü için verdiği cevaba dayanıyordu .

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.