하나의 미리듣기를 일괄 처리로 확장하기

ShellBeginner
지금 연습하기

소개

Fieldnote Audio Studio는 녹음을 짧게 들어 볼 수 있는 미리듣기 파일이 필요합니다. 먼저 3초짜리 WAV를 만들고 재생한 다음, 그 명령을 재사용 가능한 Bash 스크립트로 확장합니다. 준비된 녹음은 이 교재에서 직접 만든 합성음이며 마이크 녹음이 아닙니다.

Shell for Beginners에서 다루는 Bash의 기본 인수, 조건, 반복문, 배열, 함수를 알고 있어야 합니다. 이 실습에서는 필요한 미디어 옵션과 따옴표 사용법을 설명합니다. FFmpeg는 준비되어 있습니다. Workspace 터미널과 Listening Room 페이지는 하나의 컨테이너를 공유하며, 각 실습은 독립적으로 초기화됩니다.

미리듣기 파일을 만들고 재생하기

이 단계에서는 일괄 처리 스크립트를 작성하기 전에 올바른 미리듣기에 어떤 소리가 들어 있는지 직접 듣습니다.

UTF-8 파일명을 사용할 수 있도록 준비된 Workspace에서 시작하세요. 작업 디렉터리는 준비된 스튜디오입니다. 명령이 상대 경로를 찾을 수 없다고 하면 pwd로 현재 디렉터리를 확인하고, 다음의 완전한 cd 명령으로 돌아오세요. 이 과정의 모든 작업은 여기에서 수행합니다.

cd /home/labex/project/audio-studio
ls input

Morning bells.wav, 海辺.wav, -closing.wav가 보여야 합니다. 각 파일은 초당 22050개의 16비트 PCM 샘플을 담은 8초짜리 모노 WAV 녹음입니다. PCM은 샘플 값을 직접 저장합니다. 형식을 유지하면서 앞부분만 남기겠습니다. 지원하는 입력은 입력 디렉터리 바로 아래에 있는, 숨김 파일이 아니고 소문자 .wav로 끝나는 일반 파일입니다. 이 프로젝트는 하위 디렉터리를 재귀적으로 탐색하거나 임의의 코덱을 처리하지 않습니다.

먼저 개별 실험을 보관할 single 디렉터리를 만듭니다. mkdir -p는 필요하면 디렉터리를 만들고, 이미 존재하는 디렉터리도 허용합니다.

mkdir -p single

다음 명령에서 -i는 입력을 지정하고, -t 3은 출력을 3초로 제한하며, -c:a pcm_s16le는 부호 있는 16비트 PCM 오디오를 선택합니다. 옵션은 그 뒤에 오는 입력 또는 출력을 설명하므로 위치가 중요합니다. -v error는 일반 진행 메시지는 숨기고 오류는 표시합니다. -nostdin은 FFmpeg가 대화형 명령을 받으려고 터미널 입력을 읽지 않게 하므로 반복문 안에서도 더 안전합니다. 마지막 경로는 출력 파일의 이름입니다.

큰따옴표는 공백이 있는 경로를 하나의 인수로 유지합니다. 앞에 붙인 ./나 절대 디렉터리 경로는 -로 시작하는 파일명이 옵션으로 오해되는 것도 막습니다. 새로운 출력 파일을 사용하세요. 표준 입력을 통한 대화형 동작을 끄면 FFmpeg는 보통 기존 파일 덮어쓰기를 거부합니다.

ffmpeg -nostdin -v error -i "input/Morning bells.wav" -t 3 -c:a pcm_s16le "single/Morning bells.wav"

이 로그 수준에서는 변환에 성공해도 아무것도 출력하지 않습니다. $?는 직전 명령 자체가 반환한 종료 상태입니다. 0은 성공, 0이 아닌 값은 실패를 뜻합니다. 다른 명령을 실행하면 바뀌므로 즉시 읽어야 합니다.

printf 'Conversion exit: %s\n' "$?"

Conversion exit: 0이 나와야 합니다. 이 상태는 변환이 성공적으로 실행되었다는 뜻입니다. 파일이 실제로 사용 가능한지는 재생으로 확인합니다.

Listening Room 탭으로 전환하고 페이지를 새로 고치세요. First preview 아래 Morning bells의 재생 컨트롤을 누릅니다. 변화하는 음을 듣고 탐색 컨트롤을 클립 중간으로 움직여, 3초에서 끝나는지 확인하세요. Download WAV를 선택하면 같은 파일을 저장할 수 있습니다. 이 페이지는 실제 출력 파일만 읽으며, 없는 오디오를 라이브러리에서 채우지 않습니다. Workspace로 돌아와 계속합니다.

Listening Room에 표시된 첫 3초 미리듣기

First preview 아래의 재생·탐색 컨트롤과 Download WAV 링크를 찾으세요.

입력 경로를 잘못 썼다면 ls input으로 확인한 뒤 따옴표 안의 이름을 수정하고 명령을 다시 실행하세요. 불필요한 실험 파일이 출력 이름을 이미 차지했다면, 재시도 전에 rm -- "single/Morning bells.wav"로 그 실험 파일만 삭제합니다. --는 옵션의 끝이며 “모두 삭제”라는 뜻이 아닙니다. 이 실습에서는 library, archive, notes.txt, scratch-other를 변경하지 마세요.

변환을 재사용 가능한 함수로 만들기

이 단계에서는 녹음마다 명령을 편집하는 대신 입력, 출력, 길이를 같은 변환에 전달합니다.

Workspace의 /home/labex/project/audio-studio에서 시작하세요. 함수는 여러 명령에 이름을 붙입니다. make_preview 안의 $1, $2, $3은 그 함수의 인수입니다. 스크립트는 자신의 인수를 받아 순서대로 전달합니다. "$1"처럼 따옴표로 감싸면 공백과 Unicode를 포함한 경로 전체가 유지됩니다.

다음 히어 도큐먼트는 작은 스크립트를 씁니다. cat > preview-one.sh는 그 스크립트 파일을 교체합니다. <<'BASH'는 BASH만 있는 줄이 나올 때까지 다음 줄들을 문자 그대로 받습니다. 구분자를 따옴표로 감싸면 저장할 때 현재 셸이 $1을 확장하지 않습니다. 첫 줄은 스크립트를 프로그램으로 시작할 때 Bash를 선택합니다. 여기서는 명시적으로 bash로 실행합니다.

cat > preview-one.sh <<'BASH'
#!/usr/bin/env bash
make_preview() {
  ffmpeg -nostdin -v error -i "$1" -t "$3" -c:a pcm_s16le "$2"
}
make_preview "$1" "$2" "$3"
BASH

함수로 다른 2초 미리듣기를 만드세요. 2는 길이 인수이며 어느 파일명의 일부도 아닙니다.

bash preview-one.sh "input/海辺.wav" "single/海辺.wav" 2
printf 'Conversion exit: %s\n' "$?"

다시 0이어야 합니다. Listening Room을 새로 고치고 First preview의 海辺를 재생하여, 이번 클립은 2초에서 끝나는지 보세요. Workspace로 돌아옵니다. 같은 함수가 다른 경로와 길이에 동작합니다. 다음 단계에서는 입력 모음을 추가합니다.

모든 녹음 처리하기

이 단계에서는 입력마다 미리듣기 하나를 담은 새로운 디렉터리를 공개합니다.

Workspace에서 시작하세요. 스크립트는 입력 디렉터리, 새로운 출력 디렉터리, 1~5초의 정수 길이라는 세 인수를 정확히 받습니다. 첫 일괄 처리에서는 입력 디렉터리와 출력의 부모 디렉터리가 이미 존재하고 서로 분리되어 있어야 하며, 출력 자체는 입력 밖에 있어야 합니다. 새 실행이 좋은 결과를 덮어쓰지 않도록 기존 출력은 거부해야 합니다. 같은 출력 경로에 두 쓰기 작업을 동시에 실행하지 마세요. 이는 일반적인 오류 처리이며 동시 쓰기 안전성을 보장하지 않습니다.

작성 전에 새로운 부분을 이해하세요. realpath는 기존 입력 디렉터리를 절대 경로로 바꾸어 각 입력에 안전한 디렉터리 접두사를 줍니다. shopt -s nullglob는 일치 항목이 없는 *.wav를 별표가 든 리터럴 경로가 아닌 0개 항목으로 확장합니다. files=(...)는 일치 결과를 배열에 저장합니다. 디렉터리의 따옴표는 공백을 유지하고, 따옴표 밖의 *.wav는 여전히 파일명에 일치합니다. ${#files[@]}는 배열 원소 수를 세고, "${files[@]}"는 각 원소를 따로 전달합니다.

$(command)는 명령의 출력을 가져옵니다. 명령이 실패하면 || exit 2가 실행을 멈춥니다. 테스트 사이의 ||는 논리 OR입니다. [ -e "$2" ] || [ -L "$2" ]는 둘 중 어느 종류의 기존 대상이든 거부 사유로 봅니다.

case는 분기를 선택하고 1|2|3|4|5는 그중 한 값을 허용합니다. [ -e path ]는 대상의 존재를 확인하고, -L은 대상이 없는 심볼릭 링크도 찾습니다. ${source##*/}는 /로 끝나는 가장 긴 접두사를 제거하여 파일명만 남깁니다. if ! command는 명령이 0이 아닌 값을 반환하면 실패 분기로 갑니다. >&2는 설명을 오류 스트림에 보내고, exit 1은 실패로 종료합니다.

준비된 불완전한 batch.sh를 다음 첫 동작 버전으로 교체하세요. 이 버전은 이 실습의 정상 입력용입니다. 변환 실패는 0이 아닌 값을 반환하지만, 일부만 들어 있는 새로운 디렉터리가 남을 수 있습니다. 다음 안내형 실습에서 이런 공개를 막는 방법을 배웁니다.

cat > batch.sh <<'BASH'
#!/usr/bin/env bash
if [ "$#" -ne 3 ]; then
  printf 'Usage: batch.sh INPUT OUTPUT SECONDS\n' >&2
  exit 2
fi
case "$3" in 1|2|3|4|5) ;; *) exit 2 ;; esac
if [ -e "$2" ] || [ -L "$2" ]; then
  printf 'Output already exists\n' >&2
  exit 2
fi
input=$(realpath -- "$1") || exit 2
shopt -s nullglob
files=("$input"/*.wav)
if [ "${#files[@]}" -eq 0 ]; then
  printf 'No WAV inputs\n' >&2
  exit 1
fi
mkdir -- "$2" || exit 1
make_preview() {
  ffmpeg -nostdin -v error -i "$1" -t "$3" -c:a pcm_s16le "$2"
}
for source in "${files[@]}"; do
  name=${source##*/}
  if ! make_preview "$source" "$2/$name" "$3"; then
    printf 'Could not convert: %s\n' "$name" >&2
    exit 1
  fi
done
printf 'Published %s previews\n' "${#files[@]}"
BASH

새 previews 디렉터리에 3초 일괄 출력을 만드세요. 공개된 폴더에는 입력마다 대응하는 WAV가 원래 이름으로 정확히 하나씩 있어야 하며, 추가 파일이 없어야 합니다.

bash batch.sh input previews 3

Published 3 previews가 나와야 합니다. Listening Room을 새로 고치세요. Your listening pack에서 세 클립을 모두 재생하고, 중간 위치로 이동하고, 하나를 다운로드합니다. -closing을 포함해 각각 3초이며 자신의 원본 녹음 앞부분을 담습니다. 개별 파일 실험은 따로 남습니다.

Workspace로 돌아와 의도적으로 같은 명령을 반복하세요. 이는 예상한 거부이며 환경 고장이 아닙니다.

bash batch.sh input previews 3
printf 'Repeat exit: %s\n' "$?"

Output already exists와 Repeat exit: 2가 나와야 합니다. 정상 묶음은 보관하고, 이후 실험에서는 다른 새로운 출력 이름을 선택하세요. 빈 입력도 유용한 묶음을 만든 척하지 않고 명확히 실패할 수 있습니다. 이제 재사용 가능한 일괄 처리가 있습니다. 다음에는 녹음 하나가 손상되었을 때의 동작을 개선합니다.

요약

재생 가능한 오디오를 만들고, 따옴표로 파일명 전체를 보존하고, 함수와 배열 반복문을 재사용 가능한 Bash 일괄 처리로 결합했습니다. 명령 자체의 종료 상태를 읽고 기존 미리듣기 묶음 덮어쓰기를 거부했습니다. 다음 실습에서는 스테이징 디렉터리를 추가해 변환 실패가 불완전한 새 묶음을 공개하지 못하게 합니다.