Das Problem
docker cp kopiert genau einen Pfad und expandiert keine Wildcards:
$ docker cp "web:/var/log/app/*.log" logs/
Error response from daemon: Could not find the file /var/log/app/*.log in container web
Die übliche Umgehung startet tar im Container und entpackt den Stream auf
dem Host: docker exec CONTAINER sh -c 'tar -cf - /path/*.log' | tar -xf -.
Dafür braucht es einen laufenden Container, der sh und tar enthält. Bei
gestoppten Containern sowie bei Distroless- und FROM scratch-Images scheitert
sie deshalb.
Der Wunsch ist alt:
- moby/moby#7710, "support
wildcards and destination names in
docker cp", ist seit August 2014 offen und hat 165 Reaktionen. Ein Maintainer schrieb 2017, Beiträge seien willkommen, korrektes Wildcard-Matching sei aber knifflig. Im Thread stehen nur Umgehungen. - Zwei Fragen auf Stack Overflow, Copy folder with wildcard from docker container to host (Score 54, etwa 50.000 Aufrufe) und How to copy multiple files from container to host using docker cp (Score 20, etwa 15.000 Aufrufe).
- Verwandte offene Anfragen wünschen mehrere Quellen in einem Aufruf (moby#37961, moby#50463) und Ausschlüsse (moby#40925, 22 Reaktionen).
Wie es funktioniert
docker-cpg nutzt den Tar-Stream, den
docker cp bereits anbietet. Beim Herauskopieren nimmt es das feste
Verzeichnis vor der ersten Wildcard (/var/log/app in /var/log/app/*.log),
streamt es mit docker cp CONTAINER:/var/log/app/. - und schreibt nur die
Tar-Einträge, die passen. Im Container läuft nichts. Der Container darf also
gestoppt sein, und das Image braucht keine Shell. Beim Hineinkopieren werden
die lokalen Quellen in einen Tar-Stream gepackt und mit
docker cp - CONTAINER:DIR gesendet. Eine einzelne Quelle ohne Wildcards,
Ausschlüsse oder andere Optionen geht unverändert an docker cp.
Die Muster folgen der Shell: *, ? und [...] passen innerhalb einer
Pfadkomponente, ** passt auf beliebig viele Verzeichnisse, und Wildcards
passen auf Namen mit führendem Punkt nur, wenn das Muster selbst mit einem
Punkt beginnt. --exclude überspringt Dateien und Verzeichnisse, --parents
behält den vollen Quellpfad unter dem Ziel, und - als Ziel schreibt einen
Tar-Stream auf stdout. Das Werkzeug ist in Python ohne Abhängigkeiten
geschrieben. In ~/.docker/cli-plugins verlinkt, läuft es als docker cpg und
reicht globale Flags wie --context an seine eigenen docker-Aufrufe weiter.
Dieses Beispiel aus dem README kopiert aus einem gestoppten Container in einen Tar-Stream:
$ docker stop web
$ docker cpg "web:/var/log/app/*.log" - | tar tv
docker-cpg: copied 3 match(es), 3 file(s) to -
-rw-r--r-- 0/0 7 2026-10-30 05:09 access.log
-rw-r--r-- 0/0 6 2026-10-30 05:09 debug.log
-rw-r--r-- 0/0 6 2026-10-30 05:09 error.log
Tests
Die Testsuite umfasst 26 Tests. Die Unit-Tests decken das Musterabgleichen
(*, ?, Zeichenklassen, **, versteckte Namen), Ausschlussmuster, das
Parsen der Container-Argumente und Bedienfehler ab. Die Integrationstests
bauen in Python ein kleines Root-Dateisystem, importieren es mit
docker import und erzeugen daraus Container, die nie gestartet werden. Das
Image enthält weder eine Shell noch tar. Die Fälle umfassen symbolische
Links, ein schreibgeschütztes Verzeichnis, einen Hardlink, zwei Treffer mit
gleichem Namen, den Exit-Status 1 ohne Treffer, Probeläufe, Tar-Ausgabe auf
stdout und das Hineinkopieren mehrerer Quellen. Ein symbolischer Link, der im
Ziel schon existiert, wird ersetzt. Das Werkzeug schreibt nicht durch ihn
hindurch.
Das CI führt die Suite auf den ubuntu-24.04-Runnern von GitHub mit Python 3.9,
3.12 und 3.13 gegen Docker 28.0 aus. Dort schlagen die Integrationstests fehl,
statt übersprungen zu werden, wenn kein Docker-Daemon erreichbar ist. Ein
weiterer Schritt installiert das Werkzeug als CLI-Plugin, prüft, dass
docker --help den Befehl cpg auflistet, kopiert web:/logs/**/*.log aus
einem busybox-Container und macht einen Probelauf über
docker --context default cpg.
Grenzen
- Beim Herauskopieren wird das ganze feste Verzeichnis vor der ersten Wildcard
gestreamt und auf dem Host gefiltert.
"web:/var/log/*.log"liest ganz/var/logsamt Unterverzeichnissen,"web:/**/*.conf"das gesamte Dateisystem des Containers. - Mit Wildcard ist das Ziel immer ein Verzeichnis. Haben zwei Treffer denselben
Namen, wird der erste kopiert und die übrigen werden mit einer Warnung
übersprungen, sofern
--parentsnicht gesetzt ist. - Beim Hineinkopieren muss das Ziel ein existierendes Verzeichnis sein (eine
Regel von
docker cp -). - Kopieren zwischen zwei Containern, Klammererweiterung und das Auswerten einer
.dockerignoregibt es nicht. - Getestet ist es unter Linux mit Docker 27.0 und 28.0.
--docker podmanist nicht getestet.
Code und Installationsanleitung liegen unter github.com/Jwrede/docker-cpg.