Разделение упорядоченного списка в подсписках

У меня есть список файлов с именами prefix_0000.mp3prefix_x.mp3 , где max (x) = 9999.

У меня есть сценарий bash:

 ... sox prefix_*.mp3 script_name_output.mp3 # this fails because maximum number is 348 rm prefix_*.mp3 ... 

Как я могу лучше разделить упорядоченный список mp3-файлов в подсписках (с сохранением порядка) и постепенно sox их и удалить ненужные файлы в сценарии bash?

3 Solutions collect form web for “Разделение упорядоченного списка в подсписках”

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

 files=(prefix_????.mp3) 

Кроме того, вы можете использовать поиск и сортировку,

 IFS=$'\n' ; files=($(find . -name 'prefix_*.mp3' printf '%p\n' | sort -d)) 

Установка IFS говорит Bash разделить только на новые строки. Если ваши имена файлов и каталогов не содержат пробелов, вы можете опустить их.

Кроме того, вы можете читать имена файлов из файла, например, filelist , одно имя в строке и пустые строки,

 IFS=$'\n' files=($(<filelist)) 

Если у вас могут быть пустые строки, используйте

 IFS=$'\n' files=($(sed -e '/$/ d' filelist)) 

Затем определите, сколько файлов вы хотите в каждом фрагменте, имя временного файла аккумулятора, а также окончательное объединенное имя файла:

 s=100 src="combined-in.mp3" out="combined-out.mp3" 

Затем нам нужно просто нарезать список и обрабатывать каждый подсписчик:

 while (( ${#files[@]} > 0 )); do n=${#files[@]} # Slice files array into sub and left. if (( n <= s )); then sub=("${files[@]}") left=() else (( n-= s )) sub=("${files[@]:0:s}") left=("${files[@]:s:n}") fi # If there is no source file, but there is # a sum file, rename sum to source. if [ ! -e "$src" -a -e "$out" ]; then mv -f "$out" "$src" fi # If there is a source file, include it first. if [ -e "$src" ]; then sub=("$src" "${sub[@]}") fi # Run command. if ! sox "${sub[@]}" "$out" ; then rm -f "$out" echo "Failed!" break fi rm -f "$src" echo "Done up to ${sub[-1]}." files=("${left[@]}") # rm -f "${sub[@]}" done 

Если sox сообщает о сбое, цикл прерывается раньше. В противном случае он выведет фамилию в обработанной партии.

Мы используем if для команды sox для обнаружения сбоя и удаления выходного файла, если действительно произошел сбой. Поскольку мы также откладываем модификацию массива files до успешной команды sox , мы можем безопасно редактировать / исправлять отдельные файлы, а затем просто перезапускать цикл while, чтобы продолжить, где мы остановились.

Если на диске недостаточно места, вы можете раскомментировать вторую строку, rm -f "${sub[@]}" , чтобы удалить все файлы, которые были успешно объединены.


Вышеописанные процессы обрабатывают начальные части снова и снова.

Как я объяснил в комментарии ниже, результаты будут намного лучше, если вы сначала ffmpeg файлы, используя ffmpeg (без перекодировки с помощью sox ), возможно, после этого будет проходить перекодирование с использованием sox . (Или, конечно, вы можете перекодировать все сначала).

Во-первых, вы создаете список (строку), разделенный на трубы, имена файлов,

 files="$(ls -1 prefix_????.mp3 | tr '\n' '|')" 

удалить окончательную избыточную трубу,

 files="${files%|}" 

и подавать их в ffmpeg , без перекодирования:

 ffmpeg -i "concat:$files" -codec copy output.mp3 

Обратите внимание, что вы, возможно, захотите запустить

 ulimit -n hard 

увеличить количество открытых файлов до максимально допустимого для текущего процесса ( жесткий предел ); вы можете запросить его с помощью ulimit -n . (Я не помню, ffmpeg concat: открывает источники последовательно или все сразу.)

Если вы делаете это более одного раза, я бы поставил все это в простой скрипт:

 #!/bin/bash export LANG=C LC_ALL=C if [ $# -le 2 -o "$1" = "-h" -o "$1" = "--help" ]; then exec >&2 printf '\n' printf 'Usage: %s -h | --help ]\n' "$0" printf ' %s OUTPUT INPUT1 .. INPUTn\n' "$0" printf '\n' printf 'Inputs may be audio mp3 or MPEG media files.\n' printf '\n' exit 1 fi output="$1" shift 1 ulimit -n hard inputs="$(printf '%s|' "${@}")" inputs="${inputs%|}" ffmpeg -i "concat:$inputs" -codec copy "$output" retval=$? if [ $retval -ne 0 ]; then rm -f "$output" echo "Failed!" exit $retval fi # To remove all inputs now, uncomment the following line: # rm -f "${@}" echo "Success." exit 0 

Обратите внимание, что, поскольку я использую -codec copy вместо -codec copy -acodec copy , вышеуказанное должно работать для всех видов файлов MPEG, а не только для аудиофайлов в формате mp3.

(отредактирован для ясности и сделать его более безопасным)

Это должно работать, если в последовательности файлов нет пробелов. Просто замените LAST=0 на последнее 4-значное число в вашей последовательности. Вы останетесь с script_name_output.mp3 .

 # make a backup in case anything goes wrong mkdir backup && cp *.mp3 backup # enter last 4-digit number in the file sequence LAST=0 LASTNN__=$(echo ${LAST:0:2}) LAST__NN=$(echo ${LAST:2:2}) # sox 100 files at a time for i in $(seq -f "%02g" 0 $((--LASTNN__))); do LIST=$(paste -sd' ' <(seq -f "prefix_$i%02g.mp3" 0 99)); sox $LIST script_name_output_$i.mp3; done # sox the last group LAST_LIST=$(paste -sd' ' \ <(seq -f "prefix_${LASTNN__}%02g.mp3" 0 $LAST__NN)) sox $LAST_LIST script_name_output_${LASTNN__}.mp3 # concatenate all the sox'ed files OUTPUT_LIST=$(paste -sd' ' \ <(seq -f "script_name_output_%02g.mp3" 0 $LASTNN__)) sox $OUTPUT_LIST script_name_output.mp3 # delete the intermediate files rm $OUTPUT_LIST # delete input files if everything worked rm prefix_*.mp3 

Вы можете повысить лимит дескриптора файла:

 ulimit -n 11000 

Как обычный пользователь, вы можете повысить этот лимит до жесткого предела. См. ulimit -Hn для текущего жесткого ограничения.

Не-корневой процесс не может поднять жесткий лимит (вот в чем весь смысл, администратор устанавливает его, чтобы обычные пользователи не злоупотребляли системными ресурсами). Если у вас есть доступ к суперпользователю через sudo , вы можете запустить новую оболочку без суперпользователя с жестким и мягким пределом, поднятым с помощью:

 sudo HOME="$HOME" zsh -c 'ulimit -HSn 100000; USERNAME=$SUDO_USER; zsh' 

Или что команда sox:

 sudo HOME="$HOME" zsh -c 'ulimit -HSn 100000; USERNAME=$SUDO_USER sox prefix_*.mp3 script_name_output.mp3' 

Если в Linux вы также можете вызвать команду prlimit как root, чтобы повысить предел вашей оболочки (и ее дочерних элементов):

 bash-4.3$ ulimit -n 1024 bash-4.3$ ulimit -Hn 65536 bash-4.3$ sudo prlimit --nofile=100000:100000 --pid="$$" bash-4.3$ ulimit -Hn 100000 bash-4.3$ ulimit -n 100000 

В противном случае вы могли бы выполнить задание в 2 этапа: объединить файлы в группы из 347 файлов, а затем объединить файлы-посредники.

С zsh :

 intermediate_concat() sox "$@" intermediate.$((++n)).mp3 autoload zargs n=0 zargs -n 347 prefix_*.mp3 -- intermediate_concat sox intermediate.*.mp3(n) script_name_output.mp3 
  • Нужен скрипт для автоматизации конвертирования большого количества аудиофайлов в другой формат
  • Sox: конвертировать WAV-файл с требуемыми свойствами в одной команде
  • добавление дополнительных треков с помощью sox и командной строки
  • Как добавить аудиофайл в сценарий оболочки
  • Трубопровод Sox и FFMPEG вместе
  • Как рассчитать уровень шума аудиофайла с помощью ffmpeg?
  • Что и как кодирование необработанного (без заголовка) аудиофайла?
  • Как контролировать уровень громкости микрофона?
  • Устройство ввода-вывода по умолчанию
  • Завершить запись sox после того, как обнаружено молчание
  • Создание длинного звукового файла с SOX без фактического воспроизведения тона
  • Linux и Unix - лучшая ОС в мире.