为什么bash会在下面的循环中吃掉一些字符?
#!/bin/bash
INPUT_DIR=$1
OUTPUT_DIR=$2
rm -rf "${OUTPUT_DIR}"
mkdir -p "${OUTPUT_DIR}"
cd "${INPUT_DIR}" && find . -type f -name '*.m4a' | while IFS= read -r original
do
TMPFILE="$(mktemp --dry-run)"
IN_FILE="${INPUT_DIR}/${original}"
OUT_FILE="${OUTPUT_DIR}/${original/.m4a/.wav}"
echo "${IN_FILE} => ${TMPFILE} => ${OUT_FILE}"
(cd "${INPUT_DIR}" \
&& ffmpeg -i "${IN_FILE}" -y -f wav -acodec pcm_s16le -ac 2 "${TMPFILE}" \
)
# ... do something with "${TMPFILE}", save as ${OUT_FILE}
test -e "${TMPFILE}" && rm "${TMPFILE}"
done
当离开ffmpeg
行时,${IN_FILE}
开头的部分字符丢失,导致ffmpeg
失败(找不到文件)。 echo
命令的输出中也缺少字符。
但是,从脚本中删除ffmpeg
行时,echo
命令的输出看起来不错。
答案 0 :(得分:3)
假设在循环的第一次迭代中没有发生这种情况,很可能ffmpeg正在消耗它的一些stdin,并且它当前正在读取与while循环相同的流。只需关闭其输入:
do
TMPFILE="$(mktemp --dry-run)"
IN_FILE="${INPUT_DIR}/${original}"
OUT_FILE="${OUTPUT_DIR}/${original/.m4a/.wav}"
echo "${IN_FILE} => ${TMPFILE} => ${OUT_FILE}"
(cd "${INPUT_DIR}" \
&& ffmpeg <&1- -i "${IN_FILE}" -y -f wav -acodec pcm_s16le -ac 2 "${TMPFILE}" \
)
# ... do something with "${TMPFILE}", save as ${OUT_FILE}
test -e "${TMPFILE}" && rm "${TMPFILE}"
done
答案 1 :(得分:1)
作为将用于循环的流与循环体使用的流分离的一般方法,您可以执行以下操作:
while IFS= read -u 5 -r original
do
# Loop body
done 5< <(cd "${INPUT_DIR}" && find . -type f -name '*.m4a')
此示例使用文件描述符5(以及进程替换)。这非常有用,例如,如果你想要一个循环体来执行read
语句或其他需要用户输入的命令,那么这些命令将无法从与循环相同的流中读取{{ 1}}语句,而不是来自终端。
防止read
访问标准输入,如单独的答案所示,仍然是一个好主意,因为虽然您的循环流受到保护,但您可能仍然在上下文的标准输入上有可用的内容仍然希望保护ffmpeg
无法阅读。
此外,您可能希望对文件名使用空分隔符,这样可以处理名称中包含特殊字符的文件(即换行符)。您只需要对ffmpeg
和read
命令进行细微更改。
find