读取循环时只执行一次Shell脚本

时间:2011-04-05 08:52:55

标签: shell loops for-loop while-loop

我正在编写一个shell脚本,通过Handbrake从我的相机批处理.mov文件以节省高清空间。该脚本使用“find”搜索目录,然后在找到的每个.mov文件上运行Handbrake,将生成的文件的创建日期与源文件的日期匹配为“touch”。

我最初使用 for 循环执行此操作:

for i in $(find "$*" -iname '*.mov') ; do
  ~/Unix/HandbrakeCLI --input "$i" --output "$i".mp4 --preset="Normal"
  touch -r "$i" "$i".mp4
done

这很有效,但如果输入文件的文件名中有空格,则会失败。所以我尝试了一个循环:

find "$*" -iname '*.mov' | while read i ; do
  ~/Unix/HandbrakeCLI --input "$i" --output "$i".mp4 --preset="Normal"
  touch -r "$i" "$i".mp4
done

此循环的问题在于它适用于目录中的第一个文件,然后退出循环。请注意,如果我在while循环体中替换“echo $ i”,它会打印目录中的所有.mov文件,因此循环结构正确。

我相信我对this stackoverflow thread的问题有部分答案。但该解决方案特定于ssh,并不能解决一般问题。似乎与子进程使用的stdin有关,但我不完全理解它是如何工作的。

有什么建议吗?

我在OSX 10.6上

6 个答案:

答案 0 :(得分:2)

一种可能性是使用safe find

while IFS= read -r -d '' -u 9
do
    ~/Unix/HandbrakeCLI --input "$REPLY" --output "$REPLY".mp4 --preset="Normal"
    touch -r "$REPLY" "$REPLY".mp4
done 9< <( find "$@" -type f -print0 )

这应该与POSIX兼容,但只有HandbrakeCLItouch从标准输入读取的文件名都不包含换行符时才有效:

find "$@" -type f -print0 | while IFS= read -r
do
    ~/Unix/HandbrakeCLI --input "$REPLY" --output "$REPLY".mp4 --preset="Normal"
    touch -r "$REPLY" "$REPLY".mp4
done

答案 1 :(得分:2)

取自this answer:我现在没有回应HandbrakeCLI,以确保它不使用与我的脚本相同的标准输出:

find . -name "*.mkv" | while read FILE
do
    echo "" | handbrake-touch "$FILE"

    if [ $? != 0 ]
    then
        echo "$FILE had problems" >> errors.log  
    fi
done

......它按预期/预期工作。

答案 2 :(得分:1)

您可能正在运行shellopt'-e'(退出错误)

尝试

set +e

答案 3 :(得分:0)

此链接在Ubuntu Linux 11.04上修复了我的问题。

Preventing a child process (HandbrakeCLI) from causing the parent script to exit

以下是适用于我的代码:

ls -t *.mpeg | tail -n +2 | while read name
do echo "$name" ; in="$name" ; out=coded/"`echo $name | sed 's/.mpeg$/.mkv/'`"
echo "" | HandBrakeCLI -i "$in" -o "$out" -a '1,2' ; mv -v "$name" trash/"$name"
done

答案 4 :(得分:0)

我遇到了同样的问题。我认为HandbrakeCLI正在消耗stdin。我这里没有我的实际系统,但我的测试显示可能发生的事情。

“output”是一个数字为1-10的文件,每个文件都在各自的行上。

while read line ; do echo "MAIN: $line"; ./consumer.sh; done < output

consumer.sh:

#!/bin/sh

while read line ; do
  echo "CONSUMER: $line"
done

结果:

MAIN: 1
CONSUMER: 2
CONSUMER: 3
CONSUMER: 4
CONSUMER: 5
CONSUMER: 6
CONSUMER: 7
CONSUMER: 8
CONSUMER: 9
CONSUMER: 10

更改外部while循环将解决问题:

while read line ; do echo "MAIN: $line"; ./consumer.sh < /dev/null; done < output

结果:

MAIN: 1
MAIN: 2
MAIN: 3
MAIN: 4
MAIN: 5
MAIN: 6
MAIN: 7
MAIN: 8
MAIN: 9
MAIN: 10

答案 5 :(得分:0)

考虑使用find来调用shell,而不是将find包裹在while循环中。

find "$@" -iname '*.mov' -exec sh -c '
    ~/Unix/HandbrakeCLI --input "$1" --output "$1".mp4 --preset="Normal"
    touch -r "$1" "$1".mp4
' _ {} ';'