Bash ile dosyaları toplu olarak yeniden adlandırma


101

Bash, sürüm numaralarını kaldırmak için bir dizi paketi nasıl yeniden adlandırabilir? İkisiyle de oynuyordum exprve %%boşuna.

Örnekler:

Xft2-2.1.13.pkg olur Xft2.pkg

jasper-1.900.1.pkg olur jasper.pkg

xorg-libXrandr-1.2.3.pkg olur xorg-libXrandr.pkg


1
Bunu bir kez yazılır ve çok kullanılan bir komut dosyası olarak düzenli olarak kullanmayı düşünüyorum. Kullanacağım herhangi bir sistem üzerinde bash olacaktır, bu yüzden oldukça kullanışlı olan bashizmlerden korkmuyorum.
Jeremy L

Yanıtlar:


191

Bash'in parametre genişletme özelliğini kullanabilirsiniz.

for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done

Boşluklu dosya adları için tırnak işaretleri gereklidir.


1
Ben de beğendim, ama bash'a özgü özellikler kullanıyor ... Normalde onları "standart" sh lehine kullanmıyorum.
Diego Sevilla

2
Tek satırlık interaktif şeyler için her zaman sed-fu yerine bunu kullanırım. Eğer bir senaryo olsaydı, evet, bashizmlerden kaçının.
richq

Kodla ilgili bulduğum bir sorun: Dosyalar zaten yeniden adlandırılmışsa ve yeniden çalıştırırsam ne olur? Kendi alt dizinine geçmeye çalıştığım için uyarılar alıyorum.
Jeremy L

1
Yeniden çalıştırma hatalarını önlemek için, " .pkg" bölümünde aynı kalıbı kullanabilirsiniz , yani "for i in * - [0-9.] .Pkg; mv $ i $ {i / - [0-9 .] *. pkg / .pkg}; bitti ". Ancak hatalar yeterince zararsızdır (aynı dosyaya taşınmak).
richq

3
Bir bash çözümü değil, ancak perl yüklediyseniz, toplu yeniden adlandırma için kullanışlı yeniden adlandırma komutu sağlar, basitçe yapınrename "s/-[0-9.]*//" *.pkg
Rufus

33

Tüm dosyalar aynı dizindeyse sıra

ls | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh

işini yapacak. Sed komutu bir dizi oluşturur , mv komutlar hangi sonra olabilir kabuk içine boru. En iyisi | sh, komutun istediğinizi yaptığını doğrulamak için ardışık düzeneği takip etmeden çalıştırmak en iyisidir .

Birden çok dizini tekrarlamak için şunun gibi bir şey kullanın:

find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh

Bu Not sed sekansı gruplama normal ifade, bir sola eğik çizginin ayraçlar olup, \(ve \)daha çok tek parantez daha, (ve ).


Saflığımı affedin, ancak parantezlerden ters eğik çizgilerle kaçmak, onları gerçek karakterler olarak ele alınmasını sağlamaz mı?
devios1

2
Sed'de normal ifade gruplama dizisi (tek bir parantez yerine) şeklindedir.
Diomidis Spinellis

benim için işe yaramadı, yardımınızı almaktan memnuniyet duyarım .... FROM dosyasının soneki Kime dosyasına eklenir: $ find. -tipi d | sed -n 's /(.*)\/(.*) anysoftkeyboard (. *) / mv "\ 1 \ / \ 2anysoftkeyboard \ 3" "\ 1 \ / \ 2efectedkeyboard \ 3" / p' | sh >> >>>>> ÇIKIŞ >>>> mv: ./base/src/main/java/com/anysoftkeyboard/base/dictionaries'i ./base/src/main/java/com/effectedkeyboard/base/dictionaries/dictionaries olarak yeniden adlandırın : Böyle bir dosya veya dizin yok
Vitali Pom

Köşeli parantezlerde ters eğik çizgi eksik görünüyor.
Diomidis Spinellis

1
lsKomut dosyalarında kullanmayın . İlk çeşitlilik printf '%s\n' * | sed ...yaratıcılıkla elde edilebilir ve biraz yaratıcılıkla muhtemelen ondan da kurtulabilirsiniz sed. Bash's printf, '%q'birebir kabuk meta karakterleri içeren dosya adlarınız varsa kullanışlı olabilecek bir biçim kodu sunar .
üçlü

10

Bunun gibi bir şey yapacağım:

for file in *.pkg ; do
    mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done

tüm dosyanızın geçerli dizinde olduğunu varsayalım. Değilse, yukarıda Javier tarafından önerildiği şekilde bul özelliğini kullanmayı deneyin.

DÜZENLEME : Ayrıca, bu sürüm yukarıdaki diğerleri gibi bash'a özgü herhangi bir özellik kullanmaz, bu da sizi daha fazla taşınabilirliğe götürür.


Hepsi aynı dizinde. Rq'nin cevabı hakkında ne düşünüyorsunuz?
Jeremy L

Bash'e özgü özellikleri değil, bunun yerine daha standart sh kullanmayı seviyorum.
Diego Sevilla

1
Bunun hızlı bir yeniden adlandırma için olduğunu varsaydım, yeni nesil çapraz platform, taşınabilir Kurumsal Uygulama yazmak için değil ;-)
richq 02

1
Haha, yeterince adil ... Benim gibi taşınabilirliği düşünen bir adamdan sadece :)
Diego Sevilla

Bu, üçüncü örneği hesaba katmaz: xorg-libXrandr (isimde kullanılan tire).
Jeremy L

5

İşte şu anda kabul edilen cevaba yakın bir POSIX eşdeğeridir . Bu, ${variable/substring/replacement}Bourne uyumlu herhangi bir kabukta mevcut olan sadece Bash parametre genişletmesini değiştirir.

for i in ./*.pkg; do
    mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done

Parametre genişletmesi ${variable%pattern}, kaldırılan variableile eşleşen herhangi bir son ekin değerini üretir pattern. ( ${variable#pattern}Bir önek kaldırmak için de vardır .)

-[0-9.]*Belki yanıltıcı olsa da, alt şablonu kabul edilen cevaptan uzak tuttum . Normal bir ifade değil, glob modeli; bu nedenle "sıfır veya daha fazla sayı veya nokta izleyen bir tire" anlamına gelmez. Bunun yerine, "kısa çizgi, ardından bir sayı veya nokta, ardından herhangi bir şey" anlamına gelir. "Her şey" mümkün olan en kısa eşleşme olacak, en uzun değil. (Bash teklifler ##ve %%oldukça kısa daha olası en uzun önek veya sonek kırparak için.)


5

sedHerhangi bir * nix'de mevcut olduğunu varsayabiliriz , ancak sed -nmv komutları oluşturmayı destekleyeceğinden emin olamayız . ( NOT:sed Bunu yalnızca GNU yapar.)

Yine de, bash yerleşikler ve sed, bunu yapmak için hızlı bir şekilde bir kabuk işlevi oluşturabiliriz.

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      mv -v "$file" "$(sed $sed_pattern <<< $file)"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

Kullanım

sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.pkg

before:

./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg

after:

./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg

Hedef klasörler oluşturma:

Yana mvotomatik yaratmaz hedef klasörleri biz bizim ilk sürümünü kullanarak olamaz sedrename.

Oldukça küçük bir değişiklik, bu nedenle bu özelliği eklemek güzel olurdu:

abspathBash'de bu yapı bulunmadığından , bir yardımcı program işlevine (veya mutlak yola) ihtiyacımız olacak.

abspath () { case "$1" in
               /*)printf "%s\n" "$1";;
               *)printf "%s\n" "$PWD/$1";;
             esac; }

Buna sahip olduğumuzda, yeni klasör yapısını içeren bir sed / yeniden adlandırma modeli için hedef klasör (ler) oluşturabiliriz.

Bu, hedef klasörlerimizin adlarını bilmemizi sağlayacaktır. Yeniden adlandırdığımızda, onu hedef dosya adında kullanmamız gerekecek.

# generate the rename target
target="$(sed $sed_pattern <<< $file)"

# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"

# finally move the file to the target name/folders
mv -v "$file" "$target"

İşte tam klasör duyarlı komut dosyası ...

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      target="$(sed $sed_pattern <<< $file)"
      mkdir -p "$(dirname $(abspath $target))"
      mv -v "$file" "$target"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

Elbette, belirli hedef klasörlerimiz olmadığında da çalışıyor.

Tüm şarkıları bir klasöre koymak ./Beethoven/istersek, bunu yapabiliriz:

Kullanım

sedrename 's|Beethoven - |Beethoven/|g' *.mp3

before:

./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3

after:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

Bonus turu ...

Dosyaları klasörlerden tek bir klasöre taşımak için bu komut dosyasını kullanın:

Eşleşen tüm dosyaları toplamak ve mevcut klasöre yerleştirmek istediğimizi varsayarsak, bunu yapabiliriz:

sedrename 's|.*/||' **/*.mp3

before:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

after:

./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3

Sed normal ifade kalıplarıyla ilgili not

Bu komut dosyasında normal sed model kuralları geçerlidir, bu modeller PCRE (Perl Uyumlu Normal İfadeler) değildir. Platformunuza bağlı olarak sed -rveya sed -Ebunlardan birini kullanarak genişletilmiş düzenli ifade sözdizimi sed olabilir .

man re_formatSed temel ve genişletilmiş regexp kalıplarının tam bir açıklaması için POSIX uyumlu bakın .


4

Yeniden adlandırmanın bu tür şeyler için kullanılabilecek çok daha basit bir araç olduğunu düşünüyorum. OSX için Homebrew'de buldum

Örneğiniz için yapardım:

rename 's/\d*?\.\d*?\.\d*?//' *.pkg

'S' ikame anlamına gelir. Form s / searchPattern / replace / files_to_apply şeklindedir. Bunun için biraz çalışma gerektiren normal ifadeyi kullanmanız gerekir, ancak çabaya değer.


Katılıyorum. Kullanarak arada bir kere, forve sedvb iyidir, ama sadece kullanım için daha hızlı çok basit ve renamekendinizi sık sık bunu bulursanız.
argentum2f

Dönemlerden mi kaçıyorsun?
Big Money

Sorun, bunun taşınabilir olmamasıdır; sadece tüm platformların bir renamekomutu yoktur; ek olarak, bazı olacak farklı rename farklı özellikler, sözdizimi ve / veya seçenekleri ile komutu. Bu renameoldukça popüler olan Perl'e benziyor ; ama cevap bunu gerçekten açıklığa kavuşturmalıdır.
üçlü

3

bunun için daha iyi kullanın sed, şöyle bir şey:

find . -type f -name "*.pkg" |
 sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
 while read nameA nameB; do
    mv $nameA $nameB;
 done

normal ifadeyi bulmak bir alıştırma olarak bırakılır (boşluk içeren dosya adlarında olduğu gibi)


Teşekkürler: İsimlerde boşluk kullanılmaz.
Jeremy L

1

Bu, varsayarsak işe yarıyor gibi görünüyor

  • her şey $ pkg ile bitiyor
  • sürümünüz her zaman "-" ile başlar

.pkg dosyasını çıkarın, ardından çıkarın - ..

for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done

Adlarında kısa çizgi bulunan bazı paketler vardır (üçüncü örneğe bakın). Bu, kodunuzu etkiler mi?
Jeremy L

1
Kullanarak birden fazla sed ifadesi çalıştırabilirsiniz -e. Örn sed -e 's/\.pkg//g' -e 's/-.*//g'.
tacotuesday

lsÇıktıyı ayrıştırmayın ve değişkenlerinizi alıntı yapmayın . Kabuk betiklerinde sık karşılaşılan sorunları ve anti-modelleri teşhis etmek için shellcheck.net'e de bakın .
üçlü

1

Aynı klasörde *.txtolduğu gibi yeniden adlandırılacak birden çok dosyam vardı .sql. aşağıda benim için çalıştı:

for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done

2
Cevabınızı OP'nin gerçek kullanım durumuna soyutlayarak iyileştirebilirsiniz.
dakab

Bunun birden fazla sorunu ve belirgin bir sözdizimi hatası vardır. Başlangıç için shellcheck.net'e bakın .
üçlü

0

Bu cevaplar için teşekkür ederim. Benim de bir sorunum vardı. .Nzb.queued dosyalar .nzb dosyalarına taşınıyor. Dosya adlarında boşluklar ve diğer pürüzler vardı ve bu sorunumu çözdü:

find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh

Diomidis Spinellis'in cevabına dayanmaktadır.

Normal ifade, tüm dosya adı için bir grup ve .nzb.queued'den önceki parça için bir grup oluşturur ve ardından bir kabuk taşıma komutu oluşturur. Alıntı yapılan dizelerle. Bu aynı zamanda kabuk betiğinde bir döngü oluşturmayı da önler çünkü bu zaten sed tarafından yapılmıştır.


Bunun yalnızca GNU sed için geçerli olduğu unutulmamalıdır. BSD'lerde sed, -nseçeneği aynı şekilde yorumlamayacaktır .
ocodo
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.