2

У меня есть структура каталогов, состоящая из:

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

Я реализовал сценарий для уменьшения скорости mp3-файла до 128 кбит / с, используя lame (который одновременно работает с одним файлом).

Мой 'normalize_mp3.sh' выглядит так:

#!/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

Это прекрасно работает, если я иду папка за папкой и выполняю эту команду, но я хотел бы иметь "глобальную" команду, как в 4DOS, чтобы я мог выполнить:

$ cd iTunes/Music
$ global normalize_mp3.sh

и global команда будет проходить по всем подкаталогам и выполнять normalize_mp3.sh чтобы удалить все мои mp3 во всех подпапках.

Кто-нибудь знает, существует ли Unix, эквивалентный global команде 4DOS? Я пытался поиграть с find -exec но у меня просто болит голова.

4 ответа4

5

Другие опции:

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
2

Это очень простая реализация описанной global команды для bash:

global() {
  shopt -s globstar
  origdir="$PWD"
  for i in **/; do
    cd "$i"
    echo -n "${PWD}: "
    eval "$@"
    echo
    cd "$origdir"
  done
}
  • shopt -s globstar включает рекурсивное сглаживание с помощью ** (думаю, вам понадобится версия bash> = 4)
  • **/ находит все каталоги ниже текущего рабочего каталога, так что $i зацикливается на этих
  • Я включил echo чтобы вы могли видеть, в каком дире петля в настоящее время
  • eval "$@" оценивает все аргументы функции
  • cd $origdir возвращается в исходный каталог. (Сначала у меня был IMHO более элегантный подход с pushd / popd , но я думаю, что cd обратно в $origdir является более пуленепробиваемым в случае, если команда "$@" сама меняет каталог (что, конечно, плохая идея!)

Эта global команда принимает команды с аргументами, такими как global touch foo . Если вы хотите использовать более сложный синтаксис оболочки, такой как перенаправление или переменные, вы должны заключить команду в одинарные кавычки '...' . Но если вы хотите запустить свой скрипт normalize_mp3.sh в каждом каталоге, его использование так же просто, как

global normalize_mp3.sh

Помните, что запуску первой команды может потребоваться некоторое время, чтобы собрать все подкаталоги, если их много.

И, конечно же, пожалуйста, проверьте последнюю версию ваших файлов!


Вот как это работает (я могу тестировать только с Linux, но я предполагаю, что bash работает на OSX так же):

$ 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
...
1

Попробуйте что-то вроде:

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

Нет необходимости в настройке IFS . Вы должны поместить двойные кавычки вокруг $f хотя .. Я не знаю, есть ли у lame код возврата, если что-то пойдет не так. Возможно, сначала попробуйте выполнить команду printf "%s\n" "$f" first вместо команды lame.

1

Прежде всего, имейте немного более тонкий сценарий, который перебирает аргументы, предоставленные сценарию.

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

Чтобы он был доступен из любого места, поместите скрипт где-нибудь в $ PATH. В Linux я бы использовал $HOME/bin , который по умолчанию добавляется в $ PATH; Я подозреваю, что это верно и для OSX.

Используйте его в одном каталоге mp3s с:

downrate_mp3 ./*.mp3

Я предпочитаю ставить эти глобусы с префиксом ./ , поскольку допустимо начинать имена файлов с - , что может вызвать проблемы, так как многие программы (включая mv) ожидают, что аргументы, начинающиеся с - будут опцией, а не файлом. Для рекурсивности лучше всего использовать globstar (требуется bash 4+, но я думаю, что это то, что есть в недавнем OSX):

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

В качестве альтернативы вы можете использовать find с -exec , как вы уже пробовали:

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

Причина, по которой это не сработает с вашей существующей командой, состоит в том, что find по существу пытается использовать большой список файлов в качестве аргументов скрипта - и поскольку скрипт в вашем вопросе ничего не делает с аргументами, это явно не будет т работа.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .