Docker常用命令
帮助命令
# 显示 Docker 版本信息
docker version
# 显示系统信息,包括镜像和容器的数量
docker info
# 查看帮助文档 帮助文档地址:https://docs.docker.com/reference/
docker [命令] --help
镜像命令
查看最近创建的镜像
docker images 查看最近创建的镜像
docker images [OPTIONS] [REPOSITORY[:TAG]]
# 帮助文档
[root@hwh1 ~]# docker images --help
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
List images
Options:
-a, --all Show all images (default hides intermediate images) 显示所有镜像
--digests Show digests 显示摘要
-f, --filter filter Filter output based on conditions provided 根据提供的条件过滤输出
--format string Pretty-print images using a Go template 用 Go 模板打印出一个图像
--no-trunc Don't truncate output 不截断输出
-q, --quiet Only show numeric IDs 只显示数字 ID
# 查看最近创建的镜像
# REPOSITORY 镜像的仓库源
# TAG 镜像的标签
# IMAGE ID 镜像的id
# CREATED 镜像的创建时间
# SIZE 镜像的大小
[root@hwh1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 5 months ago 13.3kB
# 查看所有镜像
[root@hwh1 ~]# docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 5 months ago 13.3kB
# 只显示数字 ID
[root@hwh1 ~]# docker images -q
bf756fb1ae65
搜索镜像
docker search 搜索镜像
docker search [OPTIONS] TERM
[root@hwh1 ~]# docker search --help
Usage: docker search [OPTIONS] TERM
Search the Docker Hub for images
Options:
-f, --filter filter Filter output based on conditions provided 根据提供的条件过滤输出
--format string Pretty-print search using a Go template 用 Go 模板打印出一个图像
--limit int Max number of search results (default 25) 搜索结果的最大值限制
--no-trunc Don't truncate output 不截断输出
[root@hwh1 ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 9626
下载镜像
docker pull 下载镜像
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
[root@hwh1 ~]# docker pull --help
Usage: docker pull [OPTIONS] NAME[:TAG|@DIGEST]
Pull an image or a repository from a registry
Options:
-a, --all-tags Download all tagged images in the repository 下载仓库中标记所有的镜像,拿来选择版本
--disable-content-trust Skip image verification (default true) 跳过图像验证
--platform string Set platform if server is multi-platform capable 如果服务器支持多平台,则设置平台
-q, --quiet Suppress verbose output 禁止详细输出
# 下载镜像
[root@hwh1 ~]# docker pull mysql
Using default tag: latest # 版本信息(默认不设置tag,则为最新版)
latest: Pulling from library/mysql
8559a31e96f4: Pull complete # 分层下载,docker image 的核心,联合文件系统
d51ce1c2e575: Pull complete
c2344adc4858: Pull complete
fcf3ceff18fc: Pull complete
16da0c38dc5b: Pull complete
b905d1797e97: Pull complete
4b50d1c6b05c: Pull complete
c75914a65ca2: Pull complete
1ae8042bdd09: Pull complete
453ac13c00a3: Pull complete
9e680cd72f08: Pull complete
a6b5dc864b6c: Pull complete
Digest: sha256:8b7b328a7ff6de46ef96bcf83af048cb00a1c86282bfca0cb119c84568b4caf6 # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址
删除镜像
docker rmi 删除镜像
docker rmi [OPTIONS] IMAGE [IMAGE...]
[root@hwh1 ~]# docker rmi --help
Usage: docker rmi [OPTIONS] IMAGE [IMAGE...]
Remove one or more images
Options:
-f, --force Force removal of the image 强制删除镜像
--no-prune Do not delete untagged parents 不删除未标记的父类
# 删除指定镜像
[root@hwh1 ~]# docker rmi mysql
Untagged: mysql:latest
Untagged: mysql@sha256:8b7b328a7ff6de46ef96bcf83af048cb00a1c86282bfca0cb119c84568b4caf6
Deleted: sha256:be0dbf01a0f3f46fc8c88b67696e74e7005c3e16d9071032fa0cd89773771576
Deleted: sha256:086d66e8d1cb0d52e9337eabb11fb9b95960e2e1628d90100c62ea5e8bf72306
Deleted: sha256:f37c61ee1973b18c285d0d5fcf02da4bcdb1f3920981499d2a20b2858500a110
Deleted: sha256:e40b8bca7dc63fc8d188a412328e56caf179022f5e5d5b323aae57d233fb1069
Deleted: sha256:339f6b96b27eb035cbedc510adad2560132925a835f0afddbcc1d311c961c14b
Deleted: sha256:d38b06cdb26a5c98857ddbc6ef531d3f57b00e325c0c314600b712efc7ff6ab0
Deleted: sha256:09687cd9cdf4c704fde969fdba370c2d848bc614689712bef1a31d0d581f2007
Deleted: sha256:b704a4a65bf536f82e5d8b86e633d19185e26313de8380162e778feb2852011a
Deleted: sha256:c37206160543786228aa0cce738e85343173851faa44bb4dc07dc9b7dc4ff1c1
Deleted: sha256:12912c9ec523f648130e663d9d4f0a47c1841a0064d4152bcf7b2a97f96326eb
Deleted: sha256:57d29ad88aa49f0f439592755722e70710501b366e2be6125c95accc43464844
Deleted: sha256:b17c024283d0302615c6f0c825137da9db607d49a83d2215a79733afbbaeb7c3
Deleted: sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74
# 递归删除(批量删除)
[root@hwh1 ~]# docker rmi -f $(docker images -aq)
Untagged: hello-world:latest
Untagged: hello-world@sha256:6a65f928fb91fcfbc963f7aa6d57c8eeb426ad9a20c7ee045538ef34847f44f1
Deleted: sha256:bf756fb1ae65adf866bd8c456593cd24beb6a0a061dedf42b26a993176745f6b
# 已全部删除
[root@hwh1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
容器命令
注:下载一个 centos 镜像测试
新建容器并启动
docker run 新建容器并启动
[root@hwh1 ~]# docker pull centos
Using default tag: latest
latest: Pulling from library/centos
8a29a15cefae: Pull complete
Digest: sha256:fe8d824220415eed5477b63addf40fb06c3b049404242b31982106ac204f6700
Status: Downloaded newer image for centos:latest
docker.io/library/centos:latest
docker run
[root@hwh1 ~]# docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run a command in a new container
Options:
-d, --detach Run container in background and print
container ID 后台方式运行
--name string Assign a name to the container 容器名字,来区分容器
-i, --interactive Keep STDIN open even if not attached 使用交互方式,进入容器查询内容
-t, --tty Allocate a pseudo-TTY 使用交互方式,进入容器查询内容
-p, --publish list Publish a container's port(s) to the host 指定容器端口
-p 主机端口:容器端口
-p 容器端口
-p ip:主机端口:容器端口
容器端口
-P, --publish-all Publish all exposed ports to random ports 随机端口
# 启动并进入容器,相当于一个小型虚拟机
[root@hwh1 ~]# docker run -it centos /bin/bash
[root@af833bdd3acf /]# ls # 基础版 centos 很多命令都不完善
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
[root@af833bdd3acf /]# exit # 退出命令
exit
列出所有正在运行的容器
docker ps 列出所有正在运行的容器
docker ps [OPTIONS]
[root@hwh1 ~]# docker ps --help
Usage: docker ps [OPTIONS]
List containers
Options:
-a, --all Show all containers (default shows just running) 列出所有的容器,包括正在运行和停止的
-f, --filter filter Filter output based on conditions provided
--format string Pretty-print containers using a Go template
-n, --last int Show n last created containers (includes all states) 列出最近运行的容器
(default -1)
-l, --latest Show the latest created container (includes all states) 显示最后创建的容器
--no-trunc Don't truncate output 不截断输出
-q, --quiet Only display numeric IDs 只显示数字 ID
-s, --size Display total file sizes 显示文件总大小
[root@hwh1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a0f986785f33 centos "/bin/bash" 7 seconds ago Up 5 seconds nervous_agnesi
[root@hwh1 ~]# docker ps -n=1
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a0f986785f33 centos "/bin/bash" 6 minutes ago Up 6 minutes nervous_agnesi
[root@hwh1 ~]# docker ps -q
a0f986785f33
退出容器
exit # 直接容器停止并退出
Ctrl + P + Q # 不停止容器退出(快捷键)
删除容器
docker rm 删除容器
docker rm [OPTIONS] CONTAINER [CONTAINER...]
[root@hwh1 ~]# docker rm --help
Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]
Remove one or more containers
Options:
-f, --force Force the removal of a running container (uses SIGKILL) 强制删除正在运行的容器
-l, --link Remove the specified link 删除指定的链接
-v, --volumes Remove anonymous volumes associated with the container 删除与容器关联的匿名卷
[root@hwh1 ~]# docker rm af833bdd3acf 删除已经停止的
af833bdd3acf
[root@hwh1 ~]# docker rm -f a0f986785f33 删除正在运行的容器
a0f986785f33
# docker ps -a -q|xargs docker rm # 删除所有的容器,使用管道符
启动和停止容器操作
docker start id # 启动
docker restart id # 重启
docker stop id # 停止当前正在运行的容器
docker kill id # 强制停止当前容器
[root@hwh1 ~]# docker start 7b28015cd7f6
7b28015cd7f6
[root@hwh1 ~]# docker restart 7b28015cd7f6
7b28015cd7f6
[root@hwh1 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7b28015cd7f6 centos "/bin/bash" 46 seconds ago Up 9 seconds unruffled_wiles
e2ff2fee0669 bf756fb1ae65 "/hello" 12 days ago Exited (0) 12 days ago amazing_nightingale
690a9f41c7a8 bf756fb1ae65 "/hello" 12 days ago Exited (0) 12 days ago zealous_blackwell
[root@hwh1 ~]# docker stop 7b28015cd7f6
7b28015cd7f6
[root@hwh1 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7b28015cd7f6 centos "/bin/bash" 57 seconds ago Exited (0) 2 seconds ago unruffled_wiles
e2ff2fee0669 bf756fb1ae65 "/hello" 12 days ago Exited (0) 12 days ago amazing_nightingale
690a9f41c7a8 bf756fb1ae65 "/hello" 12 days ago Exited (0) 12 days ago
常用的其他命令
后台启动容器
# docker run -d 镜像名
[root@hwh1 ~]# docker run -d centos
9e34ebe17e41fb762f535ab21d81240b5fb4b105a44ed13c8813a8a8978f9b27
# 问题:
# docker ps -a,发现服务停止了
# 常见的坑:
# docker 容器使用后台运行,就必须要有要一个前台进程,docker发现没有应用,就会自动停止
查看日志
docker logs [OPTIONS] CONTAINER
[root@hwh1 ~]# docker logs --help
Usage: docker logs [OPTIONS] CONTAINER
Fetch the logs of a container
Options:
--details Show extra details provided to logs 显示额外的详细信息
-f, --follow Follow log output 跟踪日志输出
--since string Show logs since timestamp (e.g. 2013-01-02T13:23:37) or 从时间戳开始显示
relative (e.g. 42m for 42 minutes)
--tail string Number of lines to show from the end of the logs (default 日志显示行数
"all")
-t, --timestamps Show timestamps 显示时间戳
--until string Show logs before a timestamp (e.g. 2013-01-02T13:23:37) 在时间戳之前显示日志
or relative (e.g. 42m for 42 minutes)
查看容器中进程信息
docker top CONTAINER [ps OPTIONS]
[root@hwh1 ~]# docker top --help
Usage: docker top CONTAINER [ps OPTIONS]
Display the running processes of a container
# docker top 容器id
[root@hwh1 ~]# docker top 147f08710d27
UID PID PPID C STIME
root 76123 76105 0 21:13
查看镜像的元数据
docker inspect [OPTIONS] NAME|ID [NAME|ID...]
[root@hwh1 ~]# docker inspect --help
Usage: docker inspect [OPTIONS] NAME|ID [NAME|ID...]
Return low-level information on Docker objects
Options:
-f, --format string Format the output using the given Go template 用 Go 模板打印出一个图像
-s, --size Display total file sizes if the type is container 如果类型是容器的话,就显示容器大小
--type string Return JSON for specified type 返回指定类型的 JSON 字符串
# 查看 centos 的元数据
[root@hwh1 ~]# docker inspect 147f08710d27
[
{
"Id": "147f08710d2732d81ee9ce2680a7cfad48cbd186e87e27b081d82c103a29f084",
"Created": "2020-06-15T13:13:13.783652979Z",
"Path": "/bin/bash",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 76123,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-06-15T13:13:15.632825635Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:470671670cac686c7cf0081e0b37da2e9f4f768ddc5f6a26102ccd1c6954c1ee",
"ResolvConfPath": "/var/lib/docker/containers/147f08710d2732d81ee9ce2680a7cfad48cbd186e87e27b081d82c103a29f084/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/147f08710d2732d81ee9ce2680a7cfad48cbd186e87e27b081d82c103a29f084/hostname",
"HostsPath": "/var/lib/docker/containers/147f08710d2732d81ee9ce2680a7cfad48cbd186e87e27b081d82c103a29f084/hosts",
"LogPath": "/var/lib/docker/containers/147f08710d2732d81ee9ce2680a7cfad48cbd186e87e27b081d82c103a29f084/147f08710d2732d81ee9ce2680a7cfad48cbd186e87e27b081d82c103a29f084-json.log",
"Name": "/dazzling_benz",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Capabilities": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/74794286462edce8a73f8e74239c0229d6cec3afac92f052951245d91e05c7cd-init/diff:/var/lib/docker/overlay2/ae0a1afad34903571dd0df8a39bc4bea93ccd793ae2f6185fa95385c18d56f05/diff",
"MergedDir": "/var/lib/docker/overlay2/74794286462edce8a73f8e74239c0229d6cec3afac92f052951245d91e05c7cd/merged",
"UpperDir": "/var/lib/docker/overlay2/74794286462edce8a73f8e74239c0229d6cec3afac92f052951245d91e05c7cd/diff",
"WorkDir": "/var/lib/docker/overlay2/74794286462edce8a73f8e74239c0229d6cec3afac92f052951245d91e05c7cd/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "147f08710d27",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
"Image": "centos",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20200114",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"org.opencontainers.image.created": "2020-01-14 00:00:00-08:00",
"org.opencontainers.image.licenses": "GPL-2.0-only",
"org.opencontainers.image.title": "CentOS Base Image",
"org.opencontainers.image.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "dcbcaf21dc6395481ce4c27bd58e83b306aee13216f852b4ac92edab13dc6698",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/dcbcaf21dc63",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "1b8456cbbb162e74762144d5a13305ba84acb45db92ce2467c0bc15724077b72",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "23077e8fffbf9407b9d83e492e05cc11904b4dbf7fd3ab3a4b84c36c1a7ee4a5",
"EndpointID": "1b8456cbbb162e74762144d5a13305ba84acb45db92ce2467c0bc15724077b72",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]
进入当前正在运行的容器
# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
# 方式一 :docker exec -it 容器id bashShell
# 进入容器后开启一个新的终端,可以在里面操作(常用)
# docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
[root@hwh1 ~]# docker exec --help
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Run a command in a running container
Options:
-d, --detach Detached mode: run command in the background 在后台运行命令
--detach-keys string Override the key sequence for detaching a container 重写用于分离容器的键序列
-e, --env list Set environment variables 设置环境变量
-i, --interactive Keep STDIN open even if not attached 即使没有连接,也保持STDIN打开
--privileged Give extended privileges to the command 授予命令扩展权限
-t, --tty Allocate a pseudo-TTY 分配一个伪TTY
-u, --user string Username or UID (format: [:]) 用户名或者 id
-w, --workdir string Working directory inside the container 容器内的工作目录
[root@hwh1 ~]# docker exec -it a41c80fd6d1c /bin/bash
[root@a41c80fd6d1c /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
# 方式二 :docker attach 容器id
# 进入容器正在执行的终端,不会启动新的终端
# docker attach [OPTIONS] CONTAINER
[root@hwh1 ~]# docker attach --help
Usage: docker attach [OPTIONS] CONTAINER
Attach local standard input, output, and error streams to a running container
Options:
--detach-keys string Override the key sequence for detaching a container 重写用于分离容器的键序列
--no-stdin Do not attach STDIN 不要附加STDIN
--sig-proxy Proxy all received signals to the process (default true) 将所有接收到的信号代理到进程(默认为true)
[root@hwh1 ~]# docker attach a41c80fd6d1c
[root@a41c80fd6d1c /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
拷贝文件
# 从容器被拷贝文件到主机上
# docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|- docker cp 容器id:容器内路径 目的的主机路径
# 从主机上拷贝文件到容器
# docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH docker cp 主机路径 容器id:目的的容器内路径
[root@hwh1 ~]# docker cp --help
Usage: docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
Copy files/folders between a container and the local filesystem
Use '-' as the source to read a tar archive from stdin
and extract it to a directory destination in a container.
Use '-' as the destination to stream a tar archive of a
container source to stdout.
Options:
-a, --archive Archive mode (copy all uid/gid information) 存档模式(复制所有uid/gid信息)
-L, --follow-link Always follow symbol link in SRC_PATH 始终遵循容器内路径中的符号链接
# 从容器被拷贝文件到主机上
[root@hwh1 home]# docker attach a41c80fd6d1c
[root@a41c80fd6d1c /]# cd /home
[root@a41c80fd6d1c home]# ls
[root@a41c80fd6d1c home]# touch hwh.java # 在 home 目录下创建一个 hwh.java
[root@a41c80fd6d1c home]# ls
hwh.java
[root@hwh1 home]# ls
hwh hwh1 hwh2 user01 user02
[root@hwh1 home]# docker cp a41c80fd6d1c:/home/hwh.java /home # 将 hwh.java 复制到主机上
[root@hwh1 home]# ls
hwh hwh1 hwh2 hwh.java user01 user02