Shell 脚本 while read 循环仅执行一次

发布于 2024-10-29 23:31:08 字数 1020 浏览 1 评论 0原文

我正在编写一个 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

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

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 文件,因此循环结构正确。

我相信我的问题有部分答案 这个 stackoverflow 线程。但该解决方案是针对 ssh 的,并不能解决一般问题。似乎与子进程使用 stdin 有关,但我不太明白这是如何工作的。

有什么建议吗?

我使用的是 OSX 10.6

I am writing a shell script to batch process .mov files off my camera through Handbrake to save HD space. The script searches a directory with 'find' and then runs Handbrake on each .mov file found, matching the creation date of the resulting file to the source file's date with 'touch'.

I originally did this with a for loop:

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

This worked, but failed if the input files had spaces in their file names. So I tried a while loop instead:

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

The problem with this loop is that it works for the first file in the directory, and then exits the loop. Note that if I substitute an "echo $i" in the body of the while loop, it prints all of the .mov files in the directory, so the loop is structured correctly.

I believe there is a partial answer to my question on this stackoverflow thread. But the solution is specific to ssh and doesn't solve the general problem. Seems to have something do do with stdin being used by a sub-process, but I don't exactly understand how this works.

Any advice?

I'm on OSX 10.6

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

乖不如嘢 2024-11-05 23:31:08

摘自这个答案:我现在不向 HandbrakeCLI 回显任何内容,以确保它没有使用与我的脚本相同的标准输入:

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

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

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

Taken from this answer: I now echo nothing into HandbrakeCLI to ensure it's not using the same stdin as my script:

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

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

...and it works as intended/expected.

九公里浅绿 2024-11-05 23:31:08

一种可能性是使用安全查找

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

One possibility is to use the 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 )

This should be POSIX compatible, but only works if neither HandbrakeCLI nor touch read from standard input and no file names contain newlines:

find "$@" -type f -print0 | while IFS= read -r
do
    ~/Unix/HandbrakeCLI --input "$REPLY" --output "$REPLY".mp4 --preset="Normal"
    touch -r "$REPLY" "$REPLY".mp4
done
铁憨憨 2024-11-05 23:31:08

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

尝试

set +e

You are probably running with a shellopt '-e' (exit on errors)

Try

set +e
夏日浅笑〃 2024-11-05 23:31:08

此链接解决了我在 Ubuntu Linux 11.04 上的问题。

防止子进程 (HandbrakeCLI)导致父脚本退出

这是适合我的代码:

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

This link fixed the problem for me on Ubuntu Linux 11.04 .

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

Here is the code that works for me:

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
清欢 2024-11-05 23:31:08

我也有同样的问题。我认为 HandbrakeCLI 正在消耗标准输入。我这里没有实际的系统,但我的测试表明这可能会发生。

“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

I've got the same problem. I think HandbrakeCLI is consuming stdin. I don't have my actual system here, but my testing shows that's what is probably happening.

"output" is a file with the numbers 1-10, each on their own line.

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

consumer.sh:

#!/bin/sh

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

Results:

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

Changing the outer while loop will fix the problem:

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

Results:

MAIN: 1
MAIN: 2
MAIN: 3
MAIN: 4
MAIN: 5
MAIN: 6
MAIN: 7
MAIN: 8
MAIN: 9
MAIN: 10
走走停停 2024-11-05 23:31:08

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

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

Consider just using find to invoke a shell, instead of wrapping find in a while loop.

find "$@" -iname '*.mov' -exec sh -c '
    ~/Unix/HandbrakeCLI --input "$1" --output "$1".mp4 --preset="Normal"
    touch -r "$1" "$1".mp4
' _ {} ';'
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文