順序付きリストをサブリストに分割

順序付きリストをサブリストに分割

max(x) = 9999prefix_0000.mp3というファイルのリストがあります。prefix_x.mp3

Bashスクリプトがあります。

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

ソートされたmp3ファイルのリストをサブリスト(順番に保持)に分割し、徐々に削除し、soxbashスクリプトから不要なファイルを削除する最善の方法は何ですか?

答え1

まず、リストを Bash 配列として収集します。ファイルが現在のディレクトリにある場合は、次のものを使用できます。

files=(prefix_????.mp3)

または、検索と並べ替えを使用できます。

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

この設定はIFSBash に改行文字でのみ分割するよう指示します。ファイル名とディレクトリ名にスペースが含まれていない場合は省略できます。

または、ファイルからファイル名を読み取ることができます(たとえば、filelist1行に1つの名前、空白行なし)。

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ループは早期に終了します。それ以外の場合は、処理されたバッチの姓を出力します。

iffor コマンドを使用してsoxエラーを検出し、エラーが発生した場合は出力ファイルを削除します。また、コマンドが成功するまで配列の変更を延期filesするので、sox個々のファイルを安全に編集/修正してからループを再実行して、中断したwhile部分から続行できます。

ディスク容量が不足している場合は、最後から2行目のコメントを外して、正常にマージされたすべてのrm -f "${sub[@]}"ファイルを削除できます。


上記は、最初の部分を取り上げ続けています。

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

私はMPEGを使用しているので、上記は-codec copymp3-acodec copyオーディオファイルだけでなく、あらゆる種類のMPEGファイルに適用する必要があります。

答え2

(明確さを高め、より安全に編集しました)

ファイルの順序にスペースがない場合、この方法は機能します。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

答え3

ファイル記述子の制限を増やすことができます。

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'

prlimitLinuxでは、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

それ以外の場合は、ファイルを347のファイルグループに分割してリンクし、中間ファイルをリンクする2つの手順でこれを実行できます。

そして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

関連情報