Reputation: 2158
Since Docker Compose supports multiple Compose files, I want to detect all such files in the current directory and pass them to Docker Compose instead of needing to specify all of them manually. In Bash, I can accomplish this via:
docker compose $(for file in *.yml; do echo "-f $file "; done) up -d
I can produce the same list of flags in Fish with the following one-liner:
for file in *.yml; echo -n -- "-f $file "; end
I have tried various ways of passing that list to docker compose
, including the following:
docker compose (for file in *.yml; echo -n -- "-f $file "; end) up -d
… but no matter how I try, I always get the following error:
stat /home/myuser/ compose-service1.yml -f compose-service2.yml -f compose-service3.yml -f compose-service4.yml -f compose-service5.yml: no such file or directory
My questions are:
What must be done to ensure the list of option flags are passed to docker compose
without problems?
Is there another, saner approach, preferably even more concise than the (admittedly and obviously broken) approach I've taken above?
Upvotes: 1
Views: 223
Reputation: 6092
The for
loop, whilst perfectly fine, isn't strictly necessary:
fs=*.yml docker compose {-f,$fs} up -d
Upvotes: 1
Reputation: 15924
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
Upvotes: 2
Reputation: 246807
I'd do
for f in *.yml; set opts $opts "-f" $f; end
docker compose $opts up d
Suppose you have 10 files. The opts
list will have 20 entries. Then the docker compose command receives the -f option and the file as separate parameters.
Upvotes: 1