May be, the following could help to shed some light on it:
A small test program which reflects how command line arguments are passed:
$ cat >print-arg.c <<EOF
#include <stdio.h>
int main(int argc, char **argv)
{
for (int i = 0; i < argc; ++i) printf("argv[%d]: '%s'\n", i, argv[i]);
return 0;
}
EOF
$ gcc -o print-arg print-arg.c
$
This can be used to reflect arguments:
$ ./print-arg Hello\ World "Hello World"
argv[0]: './print-arg'
argv[1]: 'Hello World'
argv[2]: 'Hello World'
$
To be sure that the terminal does not fool us, we can even do this:
$ ./print-arg Hello\ World "Hello World" | hexdump -C
00000000 61 72 67 76 5b 30 5d 3a 20 27 2e 2f 70 72 69 6e |argv[0]: './prin|
00000010 74 2d 61 72 67 27 0a 61 72 67 76 5b 31 5d 3a 20 |t-arg'.argv[1]: |
00000020 27 48 65 6c 6c 6f 20 57 6f 72 6c 64 27 0a 61 72 |'Hello World'.ar|
00000030 67 76 5b 32 5d 3a 20 27 48 65 6c 6c 6f 20 57 6f |gv[2]: 'Hello Wo|
00000040 72 6c 64 27 0a |rld'.|
00000045
$
Now, this print-arg
tool can be used to debug the bash script test-subtitle.sh
:
#/bin/bash
for f in *.mp4
do
new="${f%%.mp4} (CHT).mp4"
subtitle="${f%%.mp4}.chi.ass"
./print-arg < /dev/null ffmpeg -i "$f" -vf subtitles="$subtitle" "$new"
done
Performing the test:
$ touch file1.mp4 file\ 2.mp4 "file 3.mp4"
$ ./test-subtitle.sh
argv[0]: './print-arg'
argv[1]: 'ffmpeg'
argv[2]: '-i'
argv[3]: 'file 2.mp4'
argv[4]: '-vf'
argv[5]: 'subtitles=file 2.chi.ass'
argv[6]: 'file 2 (CHT).mp4'
argv[0]: './print-arg'
argv[1]: 'ffmpeg'
argv[2]: '-i'
argv[3]: 'file 3.mp4'
argv[4]: '-vf'
argv[5]: 'subtitles=file 3.chi.ass'
argv[6]: 'file 3 (CHT).mp4'
argv[0]: './print-arg'
argv[1]: 'ffmpeg'
argv[2]: '-i'
argv[3]: 'file1.mp4'
argv[4]: '-vf'
argv[5]: 'subtitles=file1.chi.ass'
argv[6]: 'file1 (CHT).mp4'
$
This test should provide clearness how exactly ffmpeg would get the command line arguments.
Update
Please, try this on your command line:
$ < /dev/null ffmpeg -i '[アニメ] FAIRY TAIL 第001話「妖精の尻尾(ようせいのしっぽ)」(1280x720 x264 AAC).mp4' -vf 'subtitles=[アニメ] FAIRY TAIL 第001話「妖精の尻尾(ようせいのしっぽ)」(1280x720 x264 AAC).chi.ass' '[アニメ] FAIRY TAIL 第001話「妖精の尻尾(ようせいのしっぽ)」(1280x720 x264 AAC) (CHT).mp4'
After reading your answer I believe the subtitle has really to be provided backslash escaped. I'm curious whether a quoting would work instead.
So, please, do a last test on your command line:
$ < /dev/null ffmpeg -i '[アニメ] FAIRY TAIL 第001話「妖精の尻尾(ようせいのしっぽ)」(1280x720 x264 AAC).mp4' -vf 'subtitles="[アニメ] FAIRY TAIL 第001話「妖精の尻尾(ようせいのしっぽ)」(1280x720 x264 AAC).chi.ass"' '[アニメ] FAIRY TAIL 第001話「妖精の尻尾(ようせいのしっぽ)」(1280x720 x264 AAC) (CHT).mp4'
According to your comment, backslash escaping seems to be the only way to pass the subtitle file. In fact, I found a shorter (and probably more robust) solution in Stackoverflow: Escape FileNames Using The Same Way Bash Do It.
Applied to your script:
#/bin/bash
for f in *.mp4
do
new="${f%%.mp4} (CHT).mp4"
subtitle=$(printf '%q' "${f%%.mp4}.chi.ass")
< /dev/null ffmpeg -i "$f" -vf subtitles="$subtitle" "$new"
done
I changed the base script test-subtitle.sh
respectively:
#/bin/bash
for f in *.mp4
do
new="${f%%.mp4} (CHT).mp4"
subtitle=$(printf '%q' "${f%%.mp4}.chi.ass")
./print-arg < /dev/null ffmpeg -i "$f" -vf subtitles="$subtitle" "$new"
done
and got:
$ touch file1.mp4 file\ 2.mp4 "file 3.mp4" 'file[4].mp4'
$ ./test-subtitle.sh
argv[0]: './print-arg'
argv[1]: 'ffmpeg'
argv[2]: '-i'
argv[3]: 'file 2.mp4'
argv[4]: '-vf'
argv[5]: 'subtitles=file\ 2.chi.ass'
argv[6]: 'file 2 (CHT).mp4'
argv[0]: './print-arg'
argv[1]: 'ffmpeg'
argv[2]: '-i'
argv[3]: 'file 3.mp4'
argv[4]: '-vf'
argv[5]: 'subtitles=file\ 3.chi.ass'
argv[6]: 'file 3 (CHT).mp4'
argv[0]: './print-arg'
argv[1]: 'ffmpeg'
argv[2]: '-i'
argv[3]: 'file1.mp4'
argv[4]: '-vf'
argv[5]: 'subtitles=file1.chi.ass'
argv[6]: 'file1 (CHT).mp4'
argv[0]: './print-arg'
argv[1]: 'ffmpeg'
argv[2]: '-i'
argv[3]: 'file[4].mp4'
argv[4]: '-vf'
argv[5]: 'subtitles=file\[4\].chi.ass'
argv[6]: 'file[4] (CHT).mp4'
$