Fish splits command substitutions on newlines only.
You are using echo -n, which explicitly doesn't add a newline. So your option printing code
for file in *.yml; echo -n -- "-f $file "; end
ends up printing something like
-f compose-service1.yml -f compose-service2.yml -f compose-service3.yml -f compose-service4.yml -f compose-service5.yml
As one line, which, to fish, is one argument. It ends up running like (truncated)
docker compose "-f compose-service1.yml -f compose-service2.yml..." up -d
So docker tries to find one file called
compose-service1.yml -f compose-service2.yml -f compose-service3.yml -f compose-service4.yml -f compose-service5.yml
(with the leading space!)
Unlike bash, fish doesn't split this on spaces. This is generally a good idea, because people use filenames that include spaces. Your bash example is broken if a .yml file has spaces in the name.
But because you want each -f and each filename to be a separate argument to docker compose, you need to ensure that the -f and the filename end up on separate lines.
for file in *.yml; printf '%s\n' -f $file; end
or
for file in *.yml; string join \n -- -f $file; end
is probably the simplest way to do that. This would print
-f
compose-service1.yml
-f
compose-service2.yml
-f
compose-service3.yml
-f
compose-service4.yml
-f
compose-service5.yml
which fish would then turn into one argument per line.
Alternatively:
Docker's option parsing is reasonably standard, so you can also pass a compose file as -fcompose.yml, directly attached to the -f option. (this also explains your error - docker sees a -f and then uses the rest of the argument as the filename)
That would allow using echo:
for file in *.yml; echo -- "-f$file"; end
This just prepends the -f and then prints it with a newline (no echo -n), which ends up printing
-fcompose-service1.yml
-fcompose-service2.yml
(and so on)
As an aside, if you really really really need a command substitution to be split on spaces, you can use string split " ". This is common with pkg-config and other *-config tools inspired by it.
pkg-config --cflags --libs gio-2.0
prints (truncated as an example)
-pthread -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/glib-2.0
which is meant to be used as-if copy-pasted into the commandline. (this means that pkg-config paths can't include spaces, btw)
In that case you would use
g++ example_01.cpp (pkg-config --cflags --libs gio-2.0 | string split -n " ")
(the -n/--no-empty flag makes it ignore empty elements, which effectively means it treats runs of spaces as one thing).
So, if you wanted to re-do your bash command like it is, with broken spaces in filenames, you could also use
docker compose (for file in *.yml; echo -n -- "-f $file "; end | string split -n " ") up -d