Birden çok dosyayı find kullanarak nasıl yeniden adlandırılır


37

Komut findkomutunu kullanarak birden fazla dosyayı (file1 ... filen, file1_renamed ... filen_renamed) yeniden adlandırmak istiyorum :

find . -type f -name 'file*' -exec mv filename='{}' $(basename $filename)_renamed ';'

Ancak bu hatayı alıyorum:

mv: cannot stat filename=./file1’: No such file or directory

Bu çalışmıyor çünkü dosya adı kabuk değişkeni olarak yorumlanmadı.

Yanıtlar:


46

Aşağıdaki, yaklaşımınızın doğrudan düzeltilmesidir:

find . -type f -name 'file*' -exec sh -c 'x="{}"; mv "$x" "${x}_renamed"' \;

Ancak, eşleşen dosyalarınız varsa bu çok pahalıdır, çünkü mvher bir eşleşme için (a'yı çalıştıran ) taze bir kabuk başlatırsınız . Herhangi bir dosya adında komik karakterleriniz varsa, bu patlayacak. Daha verimli ve güvenli bir yaklaşım şudur:

find . -type f -name 'file*' -print0 | xargs --null -I{} mv {} {}_renamed

Ayrıca, garip olarak adlandırılmış dosyalarla çalışma avantajına da sahiptir. Eğer finddestekliyorsa, bu azaltılabilir

find . -type f -name 'file*' -exec mv {} {}_renamed \;

xargsKullanmadığınız zamanlarda versiyonu yararlıdır {}olduğu gibi,

find .... -print0 | xargs --null rm

Burada bir rmkez (veya birkaç kez çok sayıda dosyayla) çağrılır, ancak her dosya için değil.

Ben kaldırıldı basenamemuhtemelen yanlış olduğu için size, soru: Eğer hareket edeceğini foo/bar/file8için file8_renameddeğil foo/bar/file8_renamed.

Düzenlemeler (yorumlarda önerildiği gibi):

  • findOlmadan kısaltılmış eklendixargs
  • Eklenen güvenlik etiketi

Durumda xişe yaramaz: find . -type f -name 'file*' -exec mv {} "{}_renamed" \; xargsversiyon ilk örnekteki gibi aynı verimi / var
Kostas

xSadece orada doğrudan asker Nickli Üyenin yaklaşımını düzeltmek.
Thomas Erker,

2
(1) {}Doğrudan bir shell ( sh -c "…") komutunda kullanmak çok tehlikelidir - daima bir argüman olarak iletmelisiniz. (2) Yapıların tüm sürümleri yapıyı finddesteklemez {}_renamed. (3) xargsDosyaları kaldırmak için yararlı olan ifadelerinizi anlamıyorum (dosyaları yeniden adlandırmanın aksine).
G-Man,

@ G-Man: Aradaki fark vs. xargsdeğildir , ancak vs. kullanılması. Birincisi , ikincisine benzer . mvrm{}mv file1 file1_renamed; mv file2 file2_renamedrm file1 file2
Thomas Erker,

1
ve daha sonra _renamed dosyalarını tekrar orijinal isimlerine değiştirmek isterseniz, bunu nasıl yaparsınız?
mcmillab 20:18

17

İlk cevabı denedikten ve biraz ondan bahsettikten sonra, aşağıdakileri kullanarak biraz daha kısa ve daha az karmaşık yapılabileceğini öğrendim -execdir:

find . -type f -name 'file*' -execdir mv {} {}_renamed ';'

İhtiyacınız olanı da yapması gerek gibi görünüyor.


2
Bir bütün olarak finddestekleyen -execdirve {}bir bütün olarak uygulamaları ile en güvenli olanıdır. Bir eklemek isteyebilirsiniz -iiçin mvolsa (ve -Teğer mvdestekleri bunu)
Stéphane Chazelas

1
@ StéphaneChazelas daha iyi, mvbir bilgi istemek yerine ya da buna ek olarak, (uygulamanızın finddesteklenip uygulanmadığına bağlı olarak) şüphesiz, -okdiryürütmeden önce yürütülecek komutu verecek olanı kullanabilirsiniz .
Matijs

Worth söz -depthayrıca dokunmatik dizin adlarıyla olacak eğer aynı zamanda iyi bir fikirdir.
Ciro Santilli,

Argh, sadece -execdirçok sinir bozucu bir dezavantajı olduğunu buldum, göreceli yollar içeriyorsa herhangi bir findşey yapmayı reddediyoruz PATH... askubuntu.com/questions/621132/… find: The relative path XXX is included in the PATH environment
Ciro Santilli: 改造 中心 法轮功 六四 事件

6

Başka bir yaklaşım, çıktı while readüzerinde bir döngü kullanmaktır find. Bu, her bir dosya adına, ' seçeneğini sh -ckullanarak ayrı bir işlem üretmenin ek maliyet / potansiyel güvenlik sorunları hakkında endişelenmenize gerek kalmadan manipüle edilebilen bir değişken olarak erişime izin verir .find-exec

find . -type f -name 'file*' |
    while IFS= read file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

Ve kullanılan kabuk -dbir readsınırlayıcı belirtme seçeneğini destekliyorsa , aşağıdakileri kullanarak garip bir şekilde adlandırılmış dosyaları (örn. Yeni bir satırla) destekleyebilirsiniz:

find . -type f -name 'file*' -print0 |
    while IFS= read -d '' file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

3

İlk cevabı genişletmek ve ./bunun dosya adı argümanında yol öneki bulunduğundan bunun dosya adına eklenmeyeceğini not etmek istiyorum.

Thomas Erker’in cevabını değiştirerek bunu daha genel bir yaklaşım olarak görüyorum.

find . -name PATTERN -printf "%f\0" | xargs --null -I{} mv {} "prefix {} suffix"

xargs seçenekleri:

--nullHer argümanın stdin, boş bir karakter ( \0) ile bitmiş olduğunu belirtir . Bu şekilde dosya adı boşluk içerebilir, aksi takdirde her kelime mvkomut için farklı bir parametre olarak işaretlenir .

-I replace-strHer oluşumu, replace-strokunan argümanla değiştirilecektir stdin. Yani, eğer ihtiyacınız varsa, diğer string için onu değiştirebilirsiniz.


Neden bir pringf "%f\0"yerine print0?
slm

1
@sim, çünkü orijinal adları ön ekleyen bir öğeyi yeniden adlandırırken girilen kabuk meta karakterlerini içeriyorsa önek -print0üretecektir . (örneğin, adlandırmak için , için vs)./PATTERN0 - foo.txt00 - foo.txt1 - bar.txt01 - bar.txt
das-g

2

Ben benzer bir şey yapmak başardı for, findve mv.

for i in $(find . -name 'config.yml'); do mv $i $i.bak; done

Bu, tüm config.ymldosyaları bulur ve yeniden adlandırır.config.yml.bak


Bu, örneğin değişken genleşmeyi kullanabilme avantajına sahiptir. $ {i: t}. Güzel cevap
Salami,

Evet, içine hemen hemen herhangi bir ifade koyabilir forve toplu bir işlem yapabilirsiniz.
Varun Risbud
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.