【问题标题】:Syntax error: "(" unexpected -- with !(*.sh) in bash script语法错误:“(”意外 - 在 bash 脚本中带有 !(*.sh)
【发布时间】:2017-02-11 17:16:54
【问题描述】:

我要运行一个 sh 文件:

#!/bin/bash
for f in !(*.sh); do
    ffmpeg -i "$f" -vf yadif=0:-1 -threads 0 -c:v libx264 -pix_fmt yuv420p \
        -r 29.97 -b:v 3000k -s 1280x720 -preset:v slow -profile:v Main \
        -level 3.1 -bf 2 -movflags faststart /mnt/media/out-mp4/"${f%.mxf}.mp4"
    rm $f
done

但是,我收到以下错误:

2: task1.sh: Syntax error: "(" unexpected

如果我直接在命令行上尝试,它会完美运行。

路径和权限已审核

知道会发生什么吗?

【问题讨论】:

  • rm 应该在文件名参数周围使用引号,就像脚本的其余部分一样。通常,任何包含文件名的变量都需要用双引号引起来。这个问题不会出现在琐碎的文件名上,但生产代码需要处理任意文件名。

标签: linux bash ubuntu for-loop ffmpeg


【解决方案1】:

这不是一个“sh 文件”——它是一个 bash 脚本。如果您使用sh yourscript 运行它,它将无法工作(因为您尝试使用的 extglobs,您尝试使用的 shell 功能,在 POSIX sh 中不受支持);它只需要与bash yourscript 一起运行,或者在以#!/bin/bash 开头时与./yourscript 一起运行(就像它一样)。因此,将其描述为“sh 文件”具有误导性。此外,即使使用 bash,也需要打开扩展的 globbing 功能。


您的直接问题是!(*.sh) 不是常规的全局语法;它是an extglob extension,默认情况下不可用。您可能有一个 .bashrc 或类似的配置文件,可以为交互式 shell 启用此扩展,但这不适用于脚本。运行:

shopt -s extglob

...启用这些功能。


清理后,您的脚本可能如下所示:

#!/bin/bash

shopt -s extglob

# putting settings in an array allows unescaped newlines in definition
# also sorted to make it easier to find things.
settings=(
  -b:v 3000k
  -bf 2
  -c:v libx264
  -level 3.1
  -movflags faststart
  -pix_fmt yuv420p
  -preset:v slow
  -profile:v Main
  -r 29.97
  -s 1280x720
  -threads 0
  -vf yadif=0:-1
)

for f in !(*.sh); do
    ffmpeg "${settings[@]}" -i "$f" \
      /mnt/media/out-mp4/"${f%.mxf}.mp4" && rm -- "$f"
done

请注意以下更改,除了格式之外:

  • shopt -s extglob 在 glob 展开之前独占一行。
  • rm 仅在 ffmpeg 成功时运行,因为这些命令之间的分隔符是 &&,而不是 ; 或裸换行符。
  • 传递给rm-- 参数告诉它将所有未来的参数(在本例中为"$f" 的内容)视为文件名,即使它以破折号开头。
  • rm"$f" 参数在双引号内。

【讨论】:

  • 谢谢,效果很好,但是在 crontab 中 (*/2 * * * * cd /mnt/media/in-mp4/ && ./task_mp4.sh >> /tmp/task_mp4.log 2>&1) 失败 ./task_mp4.sh: line 23: ffmpeg: command not found
  • @JmvJmv,确保您设置了一个包含您的 ffmpeg 安装位置的 PATH。 cron 作业的默认 PATH 通常非常小。您可以在 crontab 的顶部设置环境变量——例如,如果您的 ffmpeg 位于 /usr/local/bin 中,您可以将 PATH=/bin:/usr/bin:/usr/local/bin 放在那里。
  • @JmvJmv, ...类似的适用于任何其他运行时依赖项——如果您的 ffmpeg 依赖于 /etc/ld.so.conf{,.d/*} 未引用的位置中的库,那么您需要在crontab 的顶部。这只是一个键值对列表,而不是 shell 脚本,所以没有export 命令或类似命令。
  • @JmvJmv, ...顺便说一句,如果您在 Linux 上,您可以轻松读取您的 cron 守护程序正在使用的 PATH。以下有点邪恶(原因与DontReadLinesWithFor有关),但是:for cron_pid in $(pgrep cron); do tr '\0' '\n' <"/proc/$cron_pid/environ" | grep -e '^PATH='; done
  • 有效,在 task_mp4.sh 的顶部添加PATH="$HOME/bin:$PATH"。但是,当我执行 MXF 转码时,我需要一个 bmx 库,在这个意义上添加 PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 在 task_mxf.sh 的顶部但不起作用 bmxtranswrap: command not found 有什么想法吗?
【解决方案2】:

您需要在脚本中启用扩展通配符:

shopt -s extglob

还要确保您没有在不同的脚本中运行脚本,例如致电sh script.sh

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    • 2015-11-09
    • 1970-01-01
    • 1970-01-01
    • 2022-11-13
    • 1970-01-01
    • 2015-07-14
    相关资源
    最近更新 更多