max(x) = 9999 prefix_0000.mp3
라는 파일 목록이 있습니다 .prefix_x.mp3
Bash 스크립트가 있습니다.
...
sox prefix_*.mp3 script_name_output.mp3 # this fails because maximum number is 348
rm prefix_*.mp3
...
정렬된 mp3 파일 목록을 하위 목록(순서 유지)으로 분할하고 점차적으로 삭제 sox
하고 bash 스크립트에서 원하지 않는 파일을 제거하는 가장 좋은 방법은 무엇입니까?
답변1
먼저 목록을 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
for 명령을 사용하여 sox
오류를 감지하고 오류가 발생하면 출력 파일을 삭제합니다. 또한 명령이 성공할 때 files
까지 배열 수정을 연기하므로 sox
개별 파일을 안전하게 편집/수정한 다음 루프를 다시 실행하여 while
중단한 부분부터 계속할 수 있습니다.
디스크 공간이 부족한 경우 마지막에서 두 번째 줄의 주석 처리를 제거하여 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 copy
mp3 -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'
prlimit
Linux에서는 루트로 다음 명령을 호출하여 셸(및 해당 하위 셸)의 제한을 늘릴 수도 있습니다 .
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개의 파일 그룹으로 분할하고 연결한 다음 중간 파일을 연결하는 두 단계로 이 작업을 수행할 수 있습니다.
그리고 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