I have a fish shell completion like this for docker:
# 'docker-subcommands.txt' is an exhaustive list derived from the "Usage" message.
set -l subcoms (cat /etc/fish/completions/docker-subcommands.txt)
# Found '__fish_seen_subcommand_from' in the completion for `systemctl`
# (in /usr/share/fish/completions/...).
complete -f -c docker -n "not __fish_seen_subcommand_from $subcoms" -a "$subcoms"
function _docker_container_action
# The placeholder is needed here because fish will choke on an empty completion list.
echo (docker ps -aq) '{z-placeholder}'
end
function _docker_image_action
echo (docker images -q) '{z-placeholder}'
end
for sc in cp exec inspect restart rm start stop
complete -c docker -n "contains $sc (commandline -poc)" -x -a (_docker_container_action)
end
for sc in rmi run
complete -c docker -n "contains $sc (commandline -poc)" -x -a (_docker_image_action)
end
The problem is that the completion for image and container IDs needs to be dynamic (like file completion), whereas the above seems to run the -a command when the completion file is (re-)sourced.
How can I make this refresh the options when the completion is actually being applied?
I should mention that I've tried quoting the -a command, as seems to be common practice. But then instead of an option for each ID, I get only one completion option, a long string with space escapes in it (\) containing the IDs and \{z-placeholder} at the end. :(
[...] Quoting the -a command does work as long as its output is not produced by echo (which stringifies it). Turns out -a's "choking" problem is not having an argument, which is what happens if you put docker ps -aq there directly and there are no container IDs to list. So this pattern works:
function _docker_container_action
docker ps -aq
end
complete -c docker -n "contains $sc (commandline -poc)" -x -a "(_docker_container_action)"
And no need for my placeholder either.
whereas the above seems to run the -a command when the completion file is (re-sourced).
That's because you've included it here as a command substitution, so when the file is sourced, it expands, and the completion system has no idea what it was before.
But the -a option knows to expand its argument at completion-time, so all you need to do is quote it:
complete -c docker -n "contains $sc (commandline -poc)" -x -a "(_docker_container_action)"
Related
I'm trying to stop all the containers starting with the name app_
I though this would work: docker stop $(docker ps -f name="app_*"), but it shows:
unknown shorthand flag: 'f' in -f
See 'docker stop --help'.
Is there a way to do this?
You must put the complete filter expression into parantheses:
docker ps -f "name=app_*"
The search is fuzzy by default, so e.g. name=app will also return my-app.
You can use a regex to indicate that the match should be at the start:
docker ps -f "name=^app_"
You should further add the quiet flag q so that the command only returns ids to make it work with docker stop:
docker stop $(docker ps -qf "name=^app_")
Your command would work, if you had the docker ps show only the ID of containers.
I started some sleeping containers like this:
for i in $(seq 1 10); do
docker run --rm -d --name sleep_$i bash sleep 100000;
done
And since they were all named sleep_* I could stop them like this:
docker ps -f Name=sleep_* --format '{{.ID}}' | xargs docker stop
I usually use xargs rather than $() when possible for this kind of thing - for one reason, I can easily parallelize it with -n 3 -P 10 (10 concurrent executions of 3 each):
$ docker ps -f Name=sleep_* --format '{{.ID}}' | xargs -n 3 -P 10 docker stop
c58a1fc538cf
98a9358734e4
e34828d3a7d7
ffbd2ec18775
1ba1e8a304e7
5e78aecb4ed6
cf86c4ea46aa
624a7d0342c2
7cb17ece1f0a
01665a084d02
Quite a lot faster for many containers.
The docker command "docker container rm $(docker ps -aq) -f" works fine from the command line. However, when I try to run it from a makefile using the following target ("remove_all_containers")...
remove_all_containers:
docker container rm $(docker ps -aq) -f
I get the error message:
host_name$ make remove_all_containers
docker container rm -f
"docker container rm" requires at least 1 argument.
See 'docker container rm --help'.
Usage: docker container rm [OPTIONS] CONTAINER [CONTAINER...]
Remove one or more containers
make: *** [remove_all_containers] Error 1
Clearly, when executed from within the makefile, the "docker ps" command is not being properly being properly executed in a way where its results can be collected and passed into the "container rm" command.
My Question: How do I get the "docker ps" command to run correctly from within the makefile and pass its results correctly into the "docker rm" command, also within the makefile?
Thanks, in advance, for any assistance you can offer.
You need a second $ in your recipe:
remove_all_containers:
docker container rm $$(docker ps -aq) -f
# ^
The single $ is expanded as a makefile variable when the makefile is parsed. It expands to blank. Make therefore passes docker container rm -f to your shell. The second $ sign causes make to expand $$ to $, and it will pass docker container rm $(docker ps -aq) -f to bash, which I'm guessing is what you want.
Notice, if you put the shell in there as #EricMd proposed, it will run a shell command, but that command will be run at Makefile read time, as opposed to the time that the recipe is executed. If the docker ps -aq command is dependent on any other artifacts of your build it would not work.
Sounds like you don't have any containers in docker to remove. I sometimes use a different syntax for this scenario:
remove_all_containers:
docker container ls -aq | xargs --no-run-if-empty docker container rm -f
The xargs syntax will not run docker container rm if there are no containers to delete.
According to the documentation, docker ps -a should list all containers.
You obtained this message "docker container rm" requires at least 1 argument certainly because you forgot to prepend the command at stake with Make's shell builtin:
remove_all_containers:
docker container rm $(shell docker ps -aq) -f
Note also that the docker ps admits a filtering feature: the online doc describes the various flavors of the corresponding -f flag.
For example, below are three Bash alias examples that can be useful to (i) stop all containers, (ii) remove all stopped containers; and (iii) remove dangling images−that would be tagged as <none> when doing docker images ls:
alias docker-stop='docker stop $(docker ps -a -q)'
alias docker-clean='docker rm $(docker ps -a -q -f status=exited)'
alias docker-purge='docker rmi $(docker images -q -f dangling=true)'
I tested for 2 way follow bellow answer:
remove_all_containers:
docker container rm $$(docker ps -aq) -f
remove_all_containers:
docker container rm $(shell docker ps -aq) -f
I'm currently working with docker config files and I'm looking for a way to automate the removal of 'old' config file. I use a script that will update all my config files version with +1;
app1-config-file-1.2.1
app1-config-file-1.2.2
app1-config-file-1.2.3
etc etc.
Now I would like to remove app1-config-file-1.2.2 when app1-config-file-1.2.3 is created.
This command gets me kinda close;
docker config rm $(docker config ls | grep -e 'app1'.*'-1.2.2')
Where 'app1' is the prefix and the '-1.2.2', well, is the version I want to remove.
The thing is that the command above does remove the correct config, but it will also try to remove the following 'configs'
Error response from daemon: access denied
Error: No such config: hours
Error: No such config: ago
Error response from daemon: access denied
Error: No such config: hours
Error: No such config: ago
Which is what comes back from my query:
$(docker config ls | grep -e 'app1'.*'-1.2.2')
I tried adding the --quiet or -q to only get back the config IDs. But for some reason I can't use it with config files the way it works with container.
docker config ls -q | grep -e 'app1'.*'-1.2.2'
results in nothing.
docker config ls | grep -e 'app1'.*'-1.2.2'
results in a list with ID, configfile names, age etc. Basically all the config files I would like to remove, but I just need the ID's
So my question is,
What is the correct way to get the IDs of specific docker config file while using wildcards?
Thank you
When you write
docker config rm $(docker config ls | grep -e 'app1'.*'-1.2.2')
This does three things:
Run docker config ls, producing its formatted output.
Filter (grep) that output to only lines that contain app1...-1.2.2.
Pass the resulting output, split into words, as arguments to docker config rm.
The ... | grep invocation is "dumb" in the sense that it doesn't know anything in particular about the input it has, it just knows that it's lines of text and it should try to filter it.
The better option here is to more directly tell docker config ls what you're looking for. That command takes a --filter option; that's somewhat underdocumented, but if you know the exact name of the config object, you can combine it with --quiet to tell Docker (not the shell) to first filter to a specific config object then only print out its ID.
docker config rm $(docker config ls -f name=app1-config-file-1.2.2 -q)
# But really this is the same as
docker config rm app1-config-file-1.2.2
The "shellier" way is to use another tool to clean up the formatted output. The examples in the Docker documentation show the config ID as the first whitespace-separated column, so you could use awk(1) like
docker config ls | \
grep 'app1.*-1.2.2' | \
awk '{ print $1 }' \
xargs docker config rm
(Other tools work fine here too; personally I'd use sed(1) but it's harder to explain in an SO answer.)
I have a Makefile with some basic docker commands:
all: build
build:
docker build .
DOCKER_CONTAINERS=$(docker ps -a -q)
DOCKER_IMAGES=$(docker images -q)
clean:
ifdef $(DOCKER_CONTAINERS)
#echo "Stopping Docker containers..."
docker stop $(DOCKER_CONTAINERS)
#echo "Deleting Docker containers..."
docker rm $(DOCKER_CONTAINERS)
endif
iclean:
ifdef $(DOCKER_IMAGES)
#echo "Deleting Docker images..."
docker rmi $(DOCKER_IMAGES)
endif
And would like to use the last one to clean existing images, I indeed have some images:
$ docker images -q
aaa5a74f6006
099dec0df83b
c9889c81a971
bddca31db222
d85328b9bf58
dd6f76d9cc90
However, DOCKER_IMAGES doesn't seem to be defined in the rule (I doubled checked that later on by displaying it):
$ make iclean
make: Nothing to be done for 'iclean'.
It works fine with clean:
$ make clean
Stopping Docker containers...
docker stop $(docker ps -a -q)
f33d30ddc991
35d4d9262460
9ac0507e0b7b
59ff62aa1c0a
2d623e844c23
cb013d3919f4
Deleting Docker containers...
docker rm $(docker ps -a -q)
f33d30ddc991
35d4d9262460
9ac0507e0b7b
59ff62aa1c0a
2d623e844c23
cb013d3919f4
What am I doing wrong here?
I see two potential problems:
$(docker images -q) invokes the make docker function... that does not exist. This evaluates as the empty string, even if you have docker images. Use:
DOCKER_IMAGES = $(shell docker images -q)
ifdef does not work as you apparently think:
ifdef $(DOCKER_IMAGES)
expands variable $(DOCKER_IMAGES) and then tests if a variable named as the result of the expansion has a value. Not what you want. You could use:
ifdef DOCKER_IMAGES
instead. But it is not a very good idea because DOCKER_IMAGES could have a value that is just blanks. What you want is test if $(DOCKER_IMAGES) is a blank string or not. Use:
ifneq ($(strip $(DOCKER_IMAGES)),)
Note that the $(strip...) is important, it removes leading and trailing blanks such that the test does not pass if $(DOCKER_IMAGES) contains only blanks.
Same remarks for your code about the running containers.
I have a script that starts 10 containers in background mode (fig up -d option). I want to aggregate the stdout or the log in /var/log from all of them. How can I do this?
The containers are started using different docker-compose files so I can not do docker-compose up target1 target2 target3
docker logs only accepts one container as a parameter.
I was considering creating a volume from the /var/log on all containers, mapping them to a directory outside of docker, making sure the logs do not have colliding name and than using the bash tail -f * . But I would appreciate a more elegant solution
This bash script will do what you want:
docker-logs
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Usage: $(basename "$0") containerid ..."
exit 1
fi
pids=()
cleanup()
{
kill "${pids[#]}"
}
trap cleanup EXIT
while [ $# -ne 0 ]
do
(docker logs -f -t --tail=10 "$1"|sed -e "s/^/$1: /")&
pids+=($!)
shift
done
wait
Usage:
$ docker-logs containerid1 containerid2 ... containeridN
The output of this script has each line from the tracked logs prepended with the container id.
The script works in --follow mode and must be interrupted with Ctrl-C.
Note that the options of docker logs are hardcoded in the script. If you need to be able to control the options of docker logs from the command line then you will need to parse the command line arguments (for example with getopts).
Docker does not support as 1.12 yet. But I have a workaround via bash;
docker ps | grep -w <filter-text> | for i in `awk '{ print $1 }'`; do docker logs -f --tail=30 $i & done
I am using docker swarm modes comes with 1.12 and deploying many replication. So all of my containers contain common text which is same as service name. To tail all of its logs in a docker node , I am using this on each docker node. filter-text will be filtering only my containers.
If you want to stop tailing, this works for me;
pkill -f 'docker logs'