0

I want to run a sh file:

#!/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

However, I get the following error:

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

If I try directly on the command line it works perfectly.

the path and permissions are already reviewed

Any idea what might be happening?

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Jmv Jmv
  • 163
  • 3
  • 8
  • 1
    The `rm` should use quotes around the file name argument just like the rest of the script. In general, any variable which holds a file name needs to be double-quoted. The problem will not manifest with trivial file names, but production code needs to cope with arbitrary file names. – tripleee Oct 03 '16 at 17:28

3 Answers3

3

This is not a "sh file" -- it's a bash script. If you run it with sh yourscript, it will not work (as extglobs, the shell feature you're trying to use, aren't supported in POSIX sh); it needs to be run only with bash yourscript, or with ./yourscript when starting with #!/bin/bash (as it does). Describing it as a "sh file" is thus misleading. Moreover, even with bash, the extended globbing feature needs to be turned on.


Your immediate issue is that !(*.sh) is not regular glob syntax; it's an extglob extension, not available by default. You may have a .bashrc or similar configuration file which enables this extension for interactive shells, but that won't apply to scripts. Run:

shopt -s extglob

...to enable these features.


Cleaned up, your script might look like:

#!/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

Note the following changes, above and beyond formatting:

  • shopt -s extglob is on its own line, before the glob is expanded.
  • The rm is only run if ffmpeg succeeds, because the separator between those commands is &&, rather than either ; or a bare newline.
  • The -- argument passed to rm tells it to treat all future arguments (in this case, the content of "$f") as a filename, even if it starts with a dash.
  • The "$f" argument to rm is inside double quotes.
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Thank you, works great however in a crontab `(*/2 * * * * cd /mnt/media/in-mp4/ && ./task_mp4.sh >> /tmp/task_mp4.log 2>&1)` fails `./task_mp4.sh: line 23: ffmpeg: command not found` – Jmv Jmv Oct 03 '16 at 19:39
  • @JmvJmv, make sure you set a `PATH` that includes your ffmpeg install location. The default PATH for cron jobs is often very minimal. You can set environment variables at the top of your crontab -- if your ffmpeg is in `/usr/local/bin`, for instance, you could put `PATH=/bin:/usr/bin:/usr/local/bin` there. – Charles Duffy Oct 03 '16 at 19:41
  • @JmvJmv, ...similar applies for any other runtime dependencies -- if your ffmpeg depends on libraries in locations not referenced by `/etc/ld.so.conf{,.d/*}`, then you'll want to set `LD_LIBRARY_PATH` at the top of the crontab. That's just a list of key-value pairs, not a shell script, so no `export` commands or similar. – Charles Duffy Oct 03 '16 at 19:43
  • @JmvJmv, ...btw, if you're on Linux, you can read the PATH that your cron daemon is using easily. The following is a bit evil (for reasons related to [DontReadLinesWithFor](http://mywiki.wooledge.org/DontReadLinesWithFor)), but: `for cron_pid in $(pgrep cron); do tr '\0' '\n' <"/proc/$cron_pid/environ" | grep -e '^PATH='; done` – Charles Duffy Oct 03 '16 at 19:54
  • Works, adding `PATH="$HOME/bin:$PATH"` at the top of task_mp4.sh. However when I do a MXF transcode, I need a bmx library in that sense add `PKG_CONFIG_PATH=/usr/local/lib/pkgconfig` at the top of task_mxf.sh but doesn't work **bmxtranswrap: command not found** Any idea ? – Jmv Jmv Oct 06 '16 at 20:21
  • `PKG_CONFIG_PATH` is generally needed at compile time, not runtime. Are you sure what you're missing isn't, say, `LD_LIBRARY_PATH`? Also do make sure `/usr/local/bin` is in your PATH, if that's where `bmxtranswrap` is. – Charles Duffy Oct 06 '16 at 20:35
  • If the problem looks like a missing dynamic library, `ldd` is your friend for debugging those problems. – Charles Duffy Oct 06 '16 at 20:36
0

You need to enable the extended globbing in the script:

shopt -s extglob

Also make sure you're not running the script in a different script, e.g. by calling sh script.sh.

choroba
  • 231,213
  • 25
  • 204
  • 289
0

I thing you are running this script sh code.sh. This means you are using sh to run the script, but the first line (#!/bin/bash) implies it's been written for bash.

On some systems sh and bash are the same, but on others they are not; and, when invoked as sh, Bash turns off some non-POSIX features. So it's important to use the right shell and the right invocation.

Use bash code.sh or better still, make the script executable (chmod a+x code.sh) and then run it directly (./code.sh)