Bash kullanarak tüm alt dizinlerde komut çalıştırma


2

Şunlardan oluşan bir dizin yapısına sahibim:

iTunes/Music/${author}/${album}/${song.mp3}

MP3 bit hızımı 128 kbps'ye çıkarmak için (o sırada tek bir dosya üzerinde çalışan) lame kullanarak bir komut dosyası kullandım.

'Normalize_mp3.sh' benim şöyle görünüyor:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for f in *.mp3
do
  lame --cbr $f __out.mp3
  mv __out.mp3 $f
done
IFS=$SAVEIFS

Bu, klasöre göre klasöre gidip bu komutu yürüttüğümde işe yarar, ancak 4DOS'taki gibi "global" bir komut almak isterim, böylece çalıştırabilirim:

$ cd iTunes/Music
$ global normalize_mp3.sh

ve globalkomut normalize_mp3.shtüm alt dizinleri geçecek ve tüm mp3'ümü tüm alt klasörlerde soymaya çalışacaktı .

4DOS globalkomutuna eşdeğer bir Unix olup olmadığını bilen var mı ? Oynamaya çalıştım find -execama başım ağrıyor.

Yanıtlar:


2

Bu, aşağıdakiler için açıklanan globalkomutun çok temel bir uygulamasıdır bash:

global() {
  shopt -s globstar
  origdir="$PWD"
  for i in **/; do
    cd "$i"
    echo -n "${PWD}: "
    eval "$@"
    echo
    cd "$origdir"
  done
}
  • shopt -s globstar özyinelemeli globbing sağlayan ile **(Ben bash versiyonunu> = 4 gerekir düşünüyorum)
  • **/Geçerli çalışma dir altındaki tüm dizinleri bulur böylece $io aşkın döngüler
  • echoDöngüsün hangi dirde olduğunu görebilmek için dahil ettim.
  • eval "$@" Fonksiyonun tüm argümanlarını değerlendirir
  • cd $origdirorijinal dizine geri döner. (İlk başta pushd/ ile IMHO'ya daha zarif bir yaklaşımım oldu popd, ancak $origdirkomutun "$@"dizini değiştirmesi durumunda cd'nin geri döndüğüne daha dayanıklı olduğunu düşünüyorum (elbette kötü bir fikir!)

Bu globalkomut, gibi argümanlarla komut alır global touch foo. Yönlendirmeler veya değişkenler gibi daha gelişmiş bir kabuk sözdizimi kullanmak istiyorsanız, komutu tek tırnak işareti içine almanız gerekir '...'. Ancak, isteğiniz komut dizinizi normalize_mp3.shher dizinde çalıştırmaksa , kullanım bu kadar basittir.

global normalize_mp3.sh

İlk komutun başlamasının, eğer birçoğu varsa tüm alt dizinleri toplamak için biraz zamana ihtiyaç duyabileceğini unutmayın.

Ve elbette, lütfen dosyalarınızın güncel bir yedeğini alın!


İşte nasıl çalıştığını (sadece Linux ile test edebiliyorum, ama bash'ın OSX üzerinde aynı şekilde çalıştığını varsayıyorum):

$ echo $BASH_VERSION 
4.1.5(1)-release
$ cd /var
$ tree -d | head
.
├── backups
├── cache
   ├── apt
      ├── apt-file
      └── archives
          └── partial
   ├── cups
      └── rss
   ├── debconf
$ global 'echo -n This command is executed in $PWD at $(date); sleep 1'
/var/backups: This command is executed in /var/backups at Mo 1. Jul 12:11:00 CEST 2013
/var/cache: This command is executed in /var/cache at Mo 1. Jul 12:11:01 CEST 2013
/var/cache/apt: This command is executed in /var/cache/apt at Mo 1. Jul 12:11:02 CEST 2013
/var/cache/apt/apt-file: This command is executed in /var/cache/apt/apt-file at Mo 1. Jul 12:11:03 CEST 2013
/var/cache/apt/archives: This command is executed in /var/cache/apt/archives at Mo 1. Jul 12:11:04 CEST 2013
/var/cache/apt/archives/partial: This command is executed in /var/cache/apt/archives/partial at Mo 1. Jul 12:11:05 CEST 2013
/var/cache/cups: This command is executed in /var/cache/cups at Mo 1. Jul 12:11:07 CEST 2013
/var/cache/cups/rss: This command is executed in /var/cache/cups/rss at Mo 1. Jul 12:11:08 CEST 2013
/var/cache/debconf: This command is executed in /var/cache/debconf at Mo 1. Jul 12:11:09 CEST 2013
...

Anladığım kadarıyla bu gerçekten özyinelemeli değil, ama denediğiniz için teşekkürler, tek kişi sizsiniz: +50
Luigi R. Viggiano

Yine de sorunun oldukça ilginç olduğunu hissediyorum ...
Luigi R. Viggiano

"Gerçekten özyinelemeli" ile ne anlıyorsunuz? Bu komut "tüm alt dizinleri aşar" yapar ve bu dizinlerin her birinde bir komut çalıştırır. Kurulumunuzla çalışmıyor mu? Çalışmasını sağlamak için en son bash sürümünü kullanmalısınız (> = 4.0) - Sanırım bash OSX üzerinde Linux'ta olduğu gibi çalışıyor mu ?!
mpy

Bu arada, komutlara değişkenleri nasıl dahil edeceğime dair bir fikrim var (cevabımı daha sonra geliştireceğim) - çok basit, ama bazen geri durmanız ve bütünün bir bakışını elde etmeniz gerekiyor.
mpy

Bu harika. Neden böyle bir şey unix utils eksik olduğunu merak ediyorum. Bu aracı geliştirmeli ve bu kaynağı açmalısınız.
Luigi R. Viggiano

5

Diğer seçenekler:

find ~/Music -name \*.mp3 | while IFS= read -r f; do
  lame --cbr "$f" "$f"_temp
  mv "$f"_temp "$f"
done
mdfind 'kMDItemAudioBitRate>128000' -onlyin ~/Music |\
parallel lame --cbr {} {}_temp \; mv {}_temp {}
f() { lame --cbr "$1" "$1"_temp; mv "$1"_temp "$1"; }
export -f f
mdfind 'kMDItemAudioBitRate>128000' -onlyin ~/Music | parallel f
shopt -s globstar # bash 4.0 or later
for f in ~/Music/**/*.mp3; do lame --cbr "$f" "$f"_temp; mv "$f"_temp "$f"; done

+1 Bu kesinlikle bir görevdir find. Komut dosyanızı değiştirmek istemeseniz bile, findkomutu yalnızca çıktı dizinleri ile sınırlayabilir ve komut dosyanızın içinden geçmesini sağlayabilirsiniz.
Squeezy

1

Gibi bir şey deneyin:

#!/bin/bash
# first cd to iTunes/Music
for f in */*/*.mp3
do 
  lame --cbr "$f" __out.mp3 && mv __out.mp3 "$f"
done

Ayarlamaya gerek yoktur IFS. $fYine de etrafına çift tırnak işareti koymanız gerekir. Eğer bir şeyler ters giderse topalın bir dönüş kodu olup olmadığını bilmiyorum. Belki de önce printf "%s\n" "$f" firsttopal komutu yerine denemek ..


1

Her şeyden önce, betiğe sağlanan argümanlardan geçen biraz daha yalın bir komut dosyası var .

#! /usr/bin/env bash
for f do
  lame --cbr "$f" "$f_out.mp3"
  mv "$f_out.mp3" "$f"
done

Herhangi bir yerden erişilebilir olması için, komut dosyasını $ PATH'inizde bir yere koyun. Linux'ta, $HOME/binvarsayılan olarak $ PATH'a eklenen ben kullanırdım ; Bunun OSX için de geçerli olduğundan şüpheleniyorum.

Tek bir mp3 dizininde kullanın:

downrate_mp3 ./*.mp3

Bu globs'ları öneklendirmeyi tercih ederim ./, çünkü dosya -isimlerinin a ile başlaması yasaldır, çünkü birçok program (dahil olmak üzere mv) -bir dosyadan ziyade bir seçenek olarak başlayan argümanları bekler . Özyinelemede en iyi yol globstar kullanmaktır (bash 4+ gerektirir, ancak sanırım OSX’in sahip olduğu şey bu):

shopt -s globstar
downrate_mp3 ./**/*.mp3

Alternatif olarak, -execdaha önce denediğiniz gibi find ile kullanabilirsiniz :

find iTunes/Music -type f -name '*.mp3' -exec downrate_mp3 {} +

Bunun mevcut komutunuzla çalışmamasının nedeni find, esas olarak büyük dosya listesini komut dosyası argümanları olarak kullanmaya çalışmaktır - ve sorunuzdaki komut dosyası argümanlarla hiçbir şey yapmadığından, bu açıkça görülmez. iş.

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.