概要
シェル上で docker ps
や docker kill
コマンドの実行にあたり、 $(コマンド)
という書き方(コマンド置換)を使うと、操作が簡単になる場合があります。たとえば、複数のコンテナをまとめて停止するためには、 docker ps
を実行後に docker kill 35e 1f5 ...
を実行する手順が必要ですが、これを docker kill $(docker ps -q)
だけで済ませます。
前提条件
- Linux や macOS 上のシェル環境で利用できます。
- Windows は PowerShell や WSL でのみ。コマンドプロンプトでは利用できません。
コマンド置換とは
あるコマンドを実行する時、そのコマンドの中で $(コマンド)
の書き方をすると、それを別のコマンド出力に置き換えられる書き方が、コマンド置換(command substitution)です。
たとえば ls
コマンドを実行すると
$ ls
file1 file2
このようにファイル一覧が出ます。
これを、コマンド置換を使えば、次のように書き換えできます。
$ echo $(ls)
file1 file2
ポイントは $(ls)
の記述とは、 ls
コマンドの表示結果( file1 file2
)を $(ls)
文字で置き換えられ、結果的に echo file1 file2
を実行したのと同じ結果となります。
$ echo file1 file2
file1 file2
なお、 `コマンド`
のようにバッククォート文字で囲む書き方もあります(例: `docker ps -q`
)。ただし、こちらはコマンド部分に `
を含められないレガシー(旧来形式)の書き方です。
Docker でコマンド置換を活用
コマンド置換を docker
コマンドで使うと、以下のような作業が簡単になります。
- 直前に作成した docker コンテナを直ちに操作する
- 実行中の Docker コンテナをすべて停止する
- Docker コンテナやイメージを全削除する
また、CI 環境などで、シェルスクリプトを利用した処理の自動化にも、このコマンド置換が利用できます。
直前に作成した Docker コンテナを直ちに操作する
Docker コンテナの起動直後、対象のコンテナに docker logs
でログ(標準出力やエラー)を見たいときや、docker exec
を使ったり、 docker top
で実行中のプロセスを表示したい場合があります。
通常であれば、2つのステップが必要です。
- 何らかの方法で、コンテナ ID またはコンテナ名を知る
-
docker
コマンドで、コンテナ ID かコンテナ名で、対象のコンテナを操作する
たとえば、 docker logs
コマンドでログを見るには docker logs interesting_sanderson
のようにコンテナ名(この例では interesting_sanderson
)、あるいは、コンテナIDを指定する必要があります。つまり、コンテナの操作には、何らかの方法で事前にコンテナ ID かコンテナ名を知っておかなくてはいけません。
ちなみに、この課題を簡単に解決するのは、 docker run --name コンテナ名
を使い、コンテナに予め名前を与える方法です。1つの Docker ホスト上で、コンテナ名を重複して実行できません。そうしておけば、シェルスクリプト上でも、間違いなくコマンドが実行できます。しかし、この方法では、同じコンテナ名で同時に処理が出来ないため、意図せず自動実行できない場合や、それを回避するための工夫が必要になる場合も考えられます。
ですが、このような方法を使わなくても、コマンド置換を使えば、直前に操作したコンテナIDを直ちに知れます。
例として、Nginx コンテナをデタッチドモード(バックグラウンド)で実行するケースを取り上げます。
$ docker run -itdP nginx
0c28d6afc0f9ba9a1f73ae7769493c437bba8583fb2c769df6ba2cb4c2202381
$
デタッチドモードであれば、コマンド実行直後に 0c28d....
で始まるコンテナ ID が表示されています。
通常、ここでログを見ようとすると docker logs 0c28
のように入力しますが、コマンド置換を使えば docker logs $(docker ps -lq)
という決まり切ったコマンドで表示できます。
$ docker logs $(docker ps -lq)
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2021/11/30 22:36:25 [notice] 1#1: using the "epoll" event method
(省略)
ここで何をしているかを知るには、 $(コマンド)
にあたる docker ps -lq
の理解が欠かせません。
まず、 docker ps -l
コマンドは docker ps --lastest
の省略系で、 -l
オプションは、直近に作成したコンテナ(状態が、起動・停止かに拘わらず)を表示します。
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0c28d6afc0f9 nginx "/docker-entrypoint.…" 6 minutes ago Up 6 minutes 0.0.0.0:49158->80/tcp, :::49158->80/tcp keen_stonebraker
そして docker ps -l -q
は docker ps --lastest --quiet
の省略系で、 -q
オプションはコンテナ ID しか表示しません。
$ docker ps -l -q
0c28d6afc0f9
つまり、 docker logs $(docker ps -lq)
の実行とは、この例における docker logs 「コマンドdocker ps -lqの実行結果」
であり、 $( )
で囲まれた部分は実行した出力に置換されますから、結果的に docker logs 0c28d6afc0f9
を実行するのと、同じ処理を意味します。
この方法を使えば、直前に操作したコンテナ ID が分からなくても、手動でコマンドを実行して確認しなくても操作できます。さらに、シェルスクリプトなど、自動的にコンテナ操作をしたい場合にも有用です。
実行中の Docker コンテナをすべて停止する
次のように、複数の Docker コンテナが起動している状態があるとします。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0c28d6afc0f9 nginx "/docker-entrypoint.…" 12 minutes ago Up 12 minutes 0.0.0.0:49158->80/tcp, :::49158->80/tcp keen_stonebraker
35ecc5c7355b nginx "/docker-entrypoint.…" 3 months ago Up 3 months 0.0.0.0:49157->80/tcp, :::49157->80/tcp inspiring_merkle
e6b88055e48c nginx "/docker-entrypoint.…" 3 months ago Up 3 months 0.0.0.0:49156->80/tcp, :::49156->80/tcp quirky_mirzakhani
1f51ba1a4dfd nginx "/docker-entrypoint.…" 3 months ago Up 3 months 0.0.0.0:49155->80/tcp, :::49155->80/tcp determined_shaw
703b701ae8d3 nginx "/docker-entrypoint.…" 3 months ago Up 3 months 0.0.0.0:49154->80/tcp, :::49154->80/tcp interesting_sanderson
7f5d7a4bd06e nginx "/docker-entrypoint.…" 3 months ago Up 3 months 0.0.0.0:49153->80/tcp, :::49153->80/tcp youthful_ramanujan
ここでは6つの Nginx コンテナが起動中です。これらをまとめて停止(kill)する場合を考えましょう。通常であれば、次の手順が必要です。
-
docker ps
でコンテナ一覧を表示する(既に実行済み) -
docker kill
で、停止したいコンテナのIDを指定する
この例であれば docker kill 0c2 35e e6b 1f5 715
と入力する必要があります。
ですが、これもコマンド置換であれば、次のコマンドで済みます。
$ docker kill $(docker ps -q)
0c28d6afc0f9
e6b88055e48c
1f51ba1a4dfd
703b701ae8d3
7f5d7a4bd06e
Docker コンテナをまとめて削除する
停止中のコンテナを全削除するには docker container prune
コマンドがあります。個人で利用しているPC上で、雑に停止中のコンテナ(正確には、コンテナ用のイメージレイヤやログや設定情報)を一括削除するには楽ですが、社内や学内の共用環境で実行するのは躊躇します。
そういう場合でも、コマンド置換と docker ps
のフィルタによって問題を解決できます。
doker ps
では -f
または --filter
オプションによって( リファレンス )、status や 元イメージの情報によるフィルタができます。
たとえば、 alpine:latest イメージを使ったコンテナ一覧は次のようになります( -a
オプションの追加により、起動・停止中に拘わらず、すべてを表示)。
$ docker ps -a -f ancestor=alpine:latest
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6873bcaf4879 alpine "/bin/sh" 2 hours ago Exited (137) 2 hours ago hardcore_tu
b5c731e558f7 alpine "/bin/sh" 2 hours ago Exited (137) 2 hours ago clever_antonelli
fa14d1f00ae7 alpine "/bin/ash" 3 months ago Exited (0) 3 months ago crazy_lehmann
c9b814f42f7c alpine "/bin/bash" 3 months ago Created pedantic_merkle
これらだけを削除するには、これまで見てきた通りコマンド置換を使えば簡単です。
$ docker rm -f $(docker ps -a -f ancestor=alpine:latest -q)
6873bcaf4879
b5c731e558f7
fa14d1f00ae7
c9b814f42f7c
実際に運用している環境では、フィルタはイメージ名だけでなく、状態など組みあわせるなど、工夫が必要になる場合もあるでしょう。
Docker イメージをまとめて削除
以前であれば、このコマンド置換を使い docker rmi -f $(docker image ls -q)
のように実行していましたが、最近では便利なコマンドがあります。
親子関係を持たない、不要と思われる中間イメージ(dangling images)を一括で消すコマンドは docker image prune
です( リファレンス )。このコマンドを実行しても、Docker Hub のようなリポジトリからダウンロードしたイメージは残しておけます。
$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest dd34e67e3371 3 months ago 133MB
alpine latest 021b3423115f 3 months ago 5.6MB
ubuntu latest 1318b700e415 4 months ago 72.8MB
hello-world latest d1165f221234 9 months ago 13.3kB
この環境では、もともと余分なイメージはなかったようです。
便利なのは、-a
オプションの付与です。Docker コンテナで使われていないイメージを削除します。さらに、 -f
オプションも付ければ確認のプロンプトも出ませんので、シェルスクリプトでの処理にも向いています。
$ docker image prune -a -f
Deleted Images:
untagged: hello-world:latest
untagged: hello-world@sha256:0fe98d7debd9049c50b597ef1f85b7c1e8cc81f59c8d623fcb2250e8bec85b38
deleted: sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726
deleted: sha256:f22b99068db93900abe17f7f5e09ec775c2826ecfe9db961fea68293744144bd
untagged: alpine:latest
untagged: alpine@sha256:eb3e4e175ba6d212ba1d6e04fc0782916c08e1c9d7b45892e9796141b1d379ae
deleted: sha256:021b3423115ff662225e83d7e2606475217de7b55fde83ce3447a54019a77aa2
deleted: sha256:bc276c40b172b1c5467277d36db5308a203a48262d5f278766cf083947d05466
Total reclaimed space: 5.609MB
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest dd34e67e3371 3 months ago 133MB
ubuntu latest 1318b700e415 4 months ago 72.8MB
この環境では nginx と ubuntu のコンテナが実行中のため残っていますが、それ以外の不要なイメージは削除されています。
ポイント
docker で $(コマンド)
によるコマンド置換を使えば、通所の作業が楽になる場合や、シェルスクリプトでのコマンド実行にも使えます。
参考
- 「Man page of BASH」の「コマンド置換」
- Command Substitution (Bash Reference Manual)