Docker容器三

Docker file镜像的制作的各种指令 ;dockerData Volume存储卷

  Docker Data Volume (存储卷)

Docker镜像由多个只读层叠加而成,启动容器时,Docker会加 载只读镜像层并在镜像栈顶部添加一个读写层

如果运行中的容器修改了现有的一个已经存在的文件,那该文 件将会从读写层下面的只读层复制到读写层,该文件的只读版 本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即 “写时复制(COW)”机制

“卷”是容器上的一个或多个“目录”,此类目录可绕过联合文件 系统,与宿主机上的某目录“绑定(关联)”

即使将容器删除,同步到宿主机上的数据也不会消失,保证数据的持久化。

QQ截图20180803192422

Docker管理卷:

Docker有两种类型的卷,每种类型都在容器中存在一个挂载点 ,但其在宿主机上的位置有所不同;

1 . Bind mount volume  (将卷绑定到宿主机)

指定容器 上的路径目录和宿主机上的目录:

docker run –name dd1 -it –rm -v /data/b2:/data  busybox (创建一个容器,前面的路径为宿主机的路径,后面为容器的路径;两个位置的路径都不需要创建会自动生成的)

docker inspect dd1  :用此命令可以查看容器的详细信息:(关连路径的详细信息)

QQ截图20180803201140

此时在容器路径下/data/创建数据,会自动同步到指定的宿主机/data/b2/路径下的。

如果将原来的容器删除,再下次创建容器时指定上一次宿主机的路径,就可以将原来的数据同步到新建的容器上了。容器的路径可以和原来不同,但宿主机的路径必须要和你要同步的数据路径相同

docker run –name dd1 -it –rm -v /data/b2:/app busybox

 

2 . Docker-managed volume (docker管理卷)

只需要指定容器里的目录,docker引擎会自动关连到宿主机上对应的路径位置,可以通过探测容器的详细信息来查询。

docker run –name dd1 -it -v /data busybox    (只指定容器的路径)

docker inspect dd1  (查看具体关连到本地的路径的信息)

QQ截图20180803195227

此时在容器里/data/下创建数据,会自动同步到宿主机上的路径下的。

有了脱离容器的持久状态。

 

两个docker容器共享一个卷:也可以多个容器共享。

在同一个宿主机上:

docker run –name dd1 -it –rm -v /data/b2:/data busybox

docker run –name dd2 -it –rm -v /data/b2:/app busybox

创建两个容器,指定相同的宿主机目录.本容器的目录可以各不相同。此时如果在一个容器的/data目录下创建一个文件,会将数据同步到本地的/data/b2目录下,也会同步到另一个容器的/app/目录下。

也可以有另一种思路:

新建一个容器,设置卷关连到宿主机。然后再设定网络环境。这样其他容器再启动时可以使用此容器的卷设置和网络设置。

 

做一个基础的容器,再创建其他容器时,复制此容器的配置。

docker run –name base -it –rm -v /data/b2:/data busybox  (创建基础容器)

docker run –name nginx –network container:base -it –rm –volumes-from base busybox  (创建一个新的容器,网路是基于base容器的;卷的关连设置也是基于base容器的)

此时查看新创建的nginx容器的网络设置和卷的设置都是和此前基础容器base的设置是相同的。

删除容器之时删除相关的卷

为docker rm命令使用-v选项 v

删除指定的卷 docker volume rm

 

 

Dockerfile  (制作容器镜像)

大多数从dockerhub上下载下来的容器镜像大多都不符合实际需求,许多配置参数都需要修改,比较麻烦,所以需要我们自己来制作符合自己需求的容器镜像。

上文介绍了以原有容器镜像为基础,来修改之后再制作成镜像的方式,此处采用另一种的制作容器镜像的方式。

dockerfile的方式是将应用的配置文件做成模板,每次运行一个镜像容器是,加载设定好的模板配置文件,就可以实现对不同镜像容器的不同需求了。

工作原理:在开启一个容器镜像时,不启动容器的主进程而是先开启另一个进程,读取模板配置文件里的参数,设定好容器主进程的各项环境变量的参数,然后再开启容器的主进程。不在通过去修改应用程序的配置文件,而是在启动程序时,给容器的应用传递环境变量。(环境变量就存放在dockerfile文件里)

FROM 指令:(指定制作镜像的基础镜像)
FROM指令是最重的一个且必须为Dockerfile文件开篇的第一个非 注释行,用于为映像文件构建过程指定基准镜像,后续的指令运 行于此基准镜像所提供的运行环境。

实践中,基准镜像可以是任何可用镜像文件,默认情况下, docker build会在docker主机上查找指定的镜像文件,在其不存在 时,则会从Docker Hub Registry上拉取所需的镜像文件

如果找不到指定的镜像文件,docker build会返回一个错误信息

form 指定镜像的格式:

FROM <image>[:<tag>] :直接指定镜像名称

FROM <image>@<digest>   :指定镜像的哈希值;(防止在使用镜像时, 被人在镜像里添加危害系统的代码)

MAINTANIER 指令:(已被废弃了)新版已经换成了LABEL

用于让镜像制作者提供本人的详细信息

Dockerfile并不限制MAINTAINER指令可在出现的位置,但推荐将 其放置于FROM指令之后

MAINTAINER <authtor’s detail> l <author’s detail>可是任何文本信息,但约定俗成地使用作者名称及邮件地址  :例如:MAINTAINER  “tom <lv@qq.com>”   (作者名和邮箱地址)

LABEL:格式是键值数据对: LABEL maintainer= “magedu <mage@magedu.com>”

COPY 指令

用于从Docker宿主机复制文件至创建的新映像文件

Syntax  格式

COPY <src> … <dest>

COPY [“<src>”,… “<dest>”]  列表格式:前面多个源文件,后面一个目标文件夹

<src>:要复制的源文件或目录,支持使用通配符

<dest>:目标路径,即正在创建的image的文件系统路径;建议为<dest>使用绝对路 径,否则,COPY指定则以WORKDIR为其起始路径;

注意:在路径中有空白字符时,通常使用第二种格式用引号。

文件复制的规则:

<src>必须是build上下文中的路径,不能是其父目录中的文件

如果<src>是目录,则其内部文件或子目录会被递归复制,但<src>目录自身 不会被复制。不复制目录,只是将目录下的文件复制过去。

如果指定了多个<src>,或在<src>中使用了通配符,则<dest>必须是一个目录 ,且必须以/结尾

如果<dest>事先不存在,它将会被自动创建,这包括其父目录路径

例如:copy index.html /data/web/html/  (仅复制一个文件到要启动容器的目录下,容器的目录如果不存在,系统会默认创建的,index.html文件的路径在当前目录下及和dockerfile文件在同一目录下)

ADD 指令

ADD指令类似于COPY指令,ADD支持使用TAR文件和URL路径

Syntax  格式

ADD <src> … <dest>

ADD [“<src>”,… “<dest>”]

操作规则:

同COPY指令

如果<src>为URL且<dest>不以/结尾,则<src>指定的文件将被下载并直接被 创建为<dest>;如果<dest>以/结尾,则文件名URL指定的文件将被直接下载 并保存为<dest>/<filename>

如果<src>是一个本地系统上的压缩格式的tar文件,它将被自动展开为一个目录 ,其行为类似于“tar -x”命令;然而通过URL网络上获取到的tar文件将不会自动 展开

如果<src>有多个,或其间接或直接使用了通配符,则<dest>必须是一个以/结 尾的目录路径;如果<dest>不以/结尾,则其被视作一个普通文件,<src>的内 容将被直接写入到<dest>;

WORKDIR 指令   (指定容器的路径)

在使用copy和add命令时,用于指定容器的路径。

用于为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和 ADD指定设定工作目录

Syntax ɰ WORKDIR <dirpath> l 在Dockerfile文件中,WORKDIR指令可出现多次,其路径也可以为相对路径,不过 ,其是相对此前一个WORKDIR指令指定的路径 l 另外,WORKDIR也可调用由ENV指定定义的变量 ɰ 例如 l WORKDIR /var/log l WORKDIR $STATEPATH

例如:

WORKDIR /usr/local/src     (先指定好容器的路径)
ADD nginx-1.15.2.tar.gz ./   (引用时只写当前路径就可以了)

VOLUME指令 (卷的挂载)

用于在image中创建一个挂载点目录,以挂载Docker host上的卷或 其它容器上的卷

Syntax  格式

VOLUME <mountpoint>

VOLUME [“<mountpoint>”]

如果挂载点目录路径下此前在文件存在,docker run命令会在卷挂 载完成后将此前的所有文件复制到新挂载的卷中

例如:VOLUME /data/mysql     (前面路径为容器里的路径,后面宿主机的目录路径不指定会自动创建的,也可以自定义的指定)

这样就实现了将容器里的/data/mysql/  目录直接就挂载到了宿主机上了。

EXPOSE 指令  (暴露端口)

用于为容器打开指定要监听的端口以实现与外部通信;只能指定自己暴露的端口,不能指定自己的IP地址。需要动态绑定到宿主机上的所有地址和随机端口,因为本机到底是运行在哪个主机上不确定,且宿主机的哪个端口空闲也是不确定的,所以是随机指定的。

Syntax

EXPOSE <port>[/<protocol>] [<port>[/<protocol>] …]

<protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议

EXPOSE指令可一次指定多个端口,例如

EXPOSE 11211/udp 11211/tcp

只写在此文件里,是不会暴露的,只有在docker run 运行时添加-P选项就会读取此文件里的暴露的配置内容,为了防止安全才这样做。

ENV指令(定义容器的环境变量)

用于为镜像定义所需的环境变量,并可被Dockerfile文件中位于其 后的其它指令(如ENV、ADD、COPY等)所调用

调用格式为$variable_name或${variable_name}

dockerfile里的变量:
${variable:-word} 当变量有值时,使用原变量的值,如果没有给与变量赋值,则使用-号后面的值。
${variable:+word} 和上述变量相反,当变量有值时,使用+后面的值,变量没有值时,就没有值了

Syntax  格式

1 .  ENV <key> <value>

2 .  ENV <key>=<value> …

第一种格式中,<key>之后的所有内容均会被视作其<value>的组成部分 ,因此,一次只能设置一个变量;

第二种格式可用一次设置多个变量,每个变量为一个”<key>=<value>”的 键值对,如果<value>中包含空格,可以以反斜线(\)进行转义,也可通过 对<value>加引号进行标识;另外,反斜线也可用于续行

定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能

例如:

第一种格式的写法,一次定义单个变量值

ENV dock_root /data/web/html/
copy index.html $dock-root  

 copy index.html ${dock-root :-/data/web/html/}  这种写法就是当变量值没有设定的时候,使用后面的字符串作为变量的值。

第二种格式的写法,一次定义多个变量值。

ENV dock_root=/data/web/html/ \    (反斜线用于换行再写,如果变量值中间有空白字符需要添加“”)

web=/data/http/dd

 

RUN指令

用于指定docker build过程中运行的程序,其可以是任何命令,但在基础镜像里需要有相对应的命令,否则也无法运行。

Syntax  格式

1 . RUN <command>

2 . RUN [“<executable>”, “<param1>”, “<param2>”]

第一种格式中,<command>通常是一个shell命令,且以“/bin/sh -c”来运行它, 这意味着此进程在容器中的PID不为1,不能接收Unix信号,因此,当使用 docker stop <container>命令停止容器时,此进程接收不到SIGTERM信号;

第二种语法格式中的参数是一个JSON格式的数组,其中<executable>为要运行的 命令,后面的<paramN>为传递给命令的选项或参数;然而,此种格式指定的命 令不会以“/bin/sh -c”来发起,因此常见的shell操作如变量替换以及通配符(?,* 等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将 其替换为类似下面的格式。

RUN [“/bin/bash”, “-c”, “<executable>”, “<param1>”]

例如:

RUN :mkdir /data/hhhhhh   (就会在容器的/data目录下新建一个hhhhhh的目录)

也可以写多个命令&&+另一条命令&&+另一条命令,尽量写在一行,因为写了多行会造成容器封层数的增加。

格式:

RUN :command1&&\

command 2 && \    (反斜线为续行在写)

command 3

 

CMD指令(定义镜像文件启动为容器时, 将定义的程序运行为容器的主程序;如果此程序结束容器也会跟随者结束)

类似于RUN指令,CMD指令也可用于运行任何命令或应用程序, 不过,二者的运行时间点不同

RUN指令运行于映像文件构建过程中,而CMD指令运行于基于Dockerfile 构建出的新映像文件启动一个容器时

CMD指令的首要目的在于为启动的容器指定默认要运行的程序,且其运 行结束后,容器也将终止;不过,CMD指定的命令其可以被docker run创建容器时后面添加的命令所覆盖掉,而不去运行dockerfile文件里定义的命令,至运行docker run 后面的命令+/bin/sh/ls /etc/  等命令。

在Dockerfile中可以存在多个CMD指令,但仅最后一个会生效

Syntax

CMD <command>

CMD [“<executable>”, “<param1>”, “<param2>”]

CMD [“<param1>”,”<param2>”]

前两种语法格式的意义同RUN

第三种则用于为ENTRYPOINT指令提供默认参数

CMD httpd -h /data/web/html  (在dockerfile文件里添加的)如果将此命令添加到以busybox的镜像里,运行时,原来默认是运行的主程序/bin/sh的,添加CMD后主程序变为httpd程序了。

第三种格式的写法:将自己定义的内容当参数传递给ENTRYPOINT的:例如:
CMD [“/bin/httpd”,”-f”,”-h /data/web/html”]
ENTROPIONT /bin/sh -c     (添加-c则指定从字符串获取参数,及允许获得CMD传送过来的参数)

 

ENTRYPOINT指令:

类似CMD指令的功能,用于为容器指定默认运行程序,从而使得 容器像是一个单独的可执行程序

与CMD不同的是,由ENTRYPOINT启动的程序不会被docker run命 令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传 递给ENTRYPOINT指定指定的程序

不过,docker run命令的–entrypoint选项的参数可覆盖ENTRYPOINT指令 指定的程序

Syntax 格式

ENTRYPOINT <command>

ENTRYPOINT [“<executable>”, “<param1>”, “<param2>”]

docker run命令传入的命令参数会覆盖CMD指令的内容并且附加到 ENTRYPOINT命令最后做为其参数使用

Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后 一个会生效

例如:
ENTRYPOINT /bin/httpd -f -h /data/web/html/ 在dockerfile文件里指定要运行的命令,且默认为主进程,在运行dockerfile容器时,docker run –name kkk -it –rm httpd:2.1 ls /data/web/html/ 其他的功能和CMD指令相同,会把其指令后面的程序运行为主程序,但和CMD不同的时在命令行运行容器时添加的命令不会运行的。而CMD会运行并将其自己指定的命令覆盖掉。

 

 

USER 指令:

用于指定运行image时的或运行Dockerfile中任何RUN、CMD或 ENTRYPOINT指令指定的程序时的用户名或UID

默认情况下,container的运行身份为root用户

Syntax 格式

USER <UID>|<UserName>

需要注意的是,<UID>可以为任意数字,但实践中其必须为/etc/passwd中 某用户的有效UID,否则,docker run命令将运行失败

ONBUILD 指令:

用于在Dockerfile中定义一个触发器

Dockerfile用于build映像文件,此映像文件亦可作为base image被 另一个Dockerfile用作FROM指令的参数,并以之构建新的映像文 件

在后面的这个Dockerfile中的FROM指令在build过程中被执行时, 将会“触发”创建其base image的Dockerfile文件中的ONBUILD指令 定义的触发器

Syntax 格式:

ONBUILD <INSTRUCTION>

尽管任何指令都可注册成为触发器指令,但ONBUILD不能自我嵌套,且不会触 发FROM和MAINTAINER指令

使用包含ONBUILD指令的Dockerfile构建的镜像应该使用特殊的标签,例如 ruby:2.0-onbuild

在ONBUILD指令中使用ADD或COPY指令应该格外小心,因为新构建过程的上 下文在缺少指定的源文件时会失败

 

 

 

 

 

 

 

 

创建镜像的示例1:(copy命令复制单个文件到容器目录下)

mkdir imge1 (创建一个文件夹)

cd imge1/    (进入到此文件夹下)

vim Dockerfile  (创建一个dockerfie文件,注意创建的文件开头字母要大写

#Description test image  (描述信息)
FROM busybox:latest      (基于什么镜像来制作)
MAINTAINER “magedu <mage@magedu.com>”   (指定镜像制作的人的信息)
#LABEL maintainer= “magedu <mage@magedu.com>”   (新的指令用法)
copy index.html /data/web/html/    (需要在imge1目录下创建一个index.html文件)

QQ截图20180804112710

vim index.html

hello

保存退出。

docker build -t httpd:1.1 imge1/  (制作镜像)

(httpd为自定义的仓库名;1.1为标签版本号;imge1/:为dockerfile文件的父目录。)

QQ截图20180804112837

docker image ls  (查看本地镜像已经生成新的镜像文件了)

基于上面制作的镜像文件来开启一个容器:

docker run –name hhh -it –rm httpd:1.1   (交互式的方式登陆进去查看dockerfile里复制到容器里的文件是否复制过去了)。

 

创建镜像示例2:(copy命令复制目录下的多个文件到容器目录里)

首先复制一个目录到img1下:

cp -r /etc/yum.repos.d  /img1/

修改dockerfile文件:

QQ截图20180804121954

其中yum.repos.d为目录。复制过去不复制目录,只复制目录下的文件,另外dockerfile文件里命令如果能写一行不要分两行来写,因为每一行都在运行容器时都是一层封装。

docker build -t httpd:1.2 /root/imge1/  (再次制作镜像)

docker run –name hhh -it –rm httpd:1.2  (以第二版本的镜像开启一个容器,查看是否将目录下的文件复制过去)

 

创建镜像示例3:(add命令下载网络上tar格式的文件。和下载本地的tar格式的文件)

首先找一个网络上的tar格式的包,复制其url的路径。

http://nginx.org/download/nginx-1.12.2.tar.gz

编辑dockerfile文件:/roo/imge1/

QQ截图20180804142226

docker build -t httpd:1.3 /root/imge1/  (再次制作镜像文件)

docker run –name kkk -it –rm httpd:1.3  (以此次的镜像文件运行一个docker容器,来验证是否将文件下载下来)

网络上的url路径下载的tar格式的包不会自动将其解压所。

但如果将tar格式的包下载到宿主机上的目录下,使用add命令,会将其自动的解压缩。

wget http://nginx.org/download/nginx-1.15.2.tar.gz  先将文件下载到本地/root/imge1/下

编辑dockerfile文件:

QQ截图20180804142955

docker build -t httpd:1.4 /root/imge1/  (创建一个新的镜像文件)

docker run –name kkk -it –rm httpd:1.4  (以此镜像运行一个容器)

进入到容器后发现,此包已经下载下来并解压缩了。

 

创建镜像示例4:(workdir指令的用法)

dockerfile文件的内容:

QQ截图20180804144422

创建镜像和运行此镜象的容器来验证参考上面的操作就可以了。

当有多个workdir路径时,从下向上找,离自己最近的workdir为自己引用的路径。

 

创建镜像示例5:(volume的挂载)

不指定宿主机的路径只指定容器的路径的挂载

编辑/root/imge1/下的dockerfile文件:

QQ截图20180804152414

docker build -t httpd:1.5 /root/imge1/(制作镜像)

docker run –name kkk -it –rm httpd:1.5  (启动容器)

docker inspect kkk  (查看容器的详细信息,容器里的目录是否挂载到宿主机上了)

QQ截图20180804152304

 

创建镜像示例6:(expose暴露端口号)

编辑/root/imge1/下的dockerfile文件:

QQ截图20180804154333

docker build -t httpd:1.6 /root/imge1/  (再次制作镜像)

docker run –name kkk -it -P  –rm httpd:1.6  (以此镜像运行一个容器)

在容器里操作

httpd -h /data/web/html  (启动http程序并指定主页面的路径)

netstat -nlt   (查看端口号是否打开)

ifconfig   (查看容器的IP地址)

最后在宿主机上curl 容器的IP地址应该可以访问容器提供的http的web服务。

docker port kkk (查看端口号是否暴露了,和查看关连到宿主机的哪个端口)

在浏览器上打开L:  宿主机IP:端口号

 

创建镜像示例7:ENV环境变量)

编辑/root/imge1/下的dockerfile文件:

QQ截图20180804172342

docker build -t httpd:1.7 /root/imge1/ (再次制作镜像)
docker run –name kkk -it -P –rm httpd:1.7 (以此镜像运行一个容器)

进入容器后查看是否完成。

docker run –name kkk -it -P –rm httpd:1.7 printenv (查看环境变量信息)

docker run –name kkk -it -P -e web=”nginx-1.15.1.tar.gz” –rm httpd:1.7  (也可以在build时重新来定义其变量的值)

 

创建镜像示例8: (run指令;前提是基础容器里有此命令)

编辑/root/imge1/下的dockerfile文件:

QQ截图20180804175228

docker build -t httpd:1.8 /root/imge1/  (再次制作镜像)

docker run –name kkk -it -P –rm httpd:1.8  (基于此镜像开启容器)

查看容器验证run命令是否执行成功。

创建镜像示例8:( ENTRYPOINT指令)

编辑/root/imge1/下的dockerfile文件:
QQ截图20180805145602

docker build -t httpd:2.1 /root/imge1/  (再次制作新的镜像)

docker run –name kkk -it –rm httpd:2.1  (以此镜像为基础开启一个新的容器;默认就会运行httpd的程序,而不是原本busybox的/bin/sh的程序了)

docker run –name kkk -it –rm httpd:2.1 ls /data/web/html/  (但是和CMD不同的是 ,命令行后面的 命令与不会运行,而CMD定义的主程序,如果命令行也有命令,则运行命令行上的命令)

如果一定要想运行自己的命令:需要指定专门的选项:
docker run –name kkk -it –rm –entrypoint “ls /data/web/html” httpd:2.1  (这样写就能将定义的entrypoint的命令覆盖掉)。

CMD [“/usr/sbin/nginx”,”-g”,”daemon off;”]   (daemon off:运行为前端,在dockerfile文件里)

/usr/sbin/nginx -g “daemon off;”   在命令行里

创建镜像示例8:(使用entrypoint来传递变量的参数)

mkdir /root/img3   (创建一个文件夹)

vim /root/img3/Dockerfile   (创建dockerfile的配置文件)

QQ截图20180805214920

vim entrypoint.sh  (编写一个脚本)

QQ截图20180805215017

添加脚本的执行权限。

docker build -t nginx:2.3 /root/img3/  (创建新的镜像)

docker run –name kk –rm nginx:2.3  (以此镜像为基础开启一个容器)

 

 

 

 

 

 

 

 

 

 

 

 

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/104466

(0)
无所谓无所谓
上一篇 2018-08-02 23:38
下一篇 2018-08-04

相关推荐

  • 第四个礼拜作业

    1、复制/etc/skel目录为/home/tuser1,要求/home/tuser1及其内部文件的属组和其它用户均没有任何访问权限。         cp -r /etc/skel /home/tuser1 ;         ll -d /home/tuser1;         chmod -R g-rwx,o-rwx /home/tuser1;   …

    Linux笔记 2018-06-03
  • nginx实现请求转发

    反向代理适用于很多场合,负载均衡是最普遍的用法。 nginx 作为目前最流行的web服务器之一,可以很方便地实现反向代理。 nginx 反向代理官方文档: NGINX REVERSE PROXY 当在一台主机上部署了多个不同的web服务器,并且需要能在80端口同时访问这些web服务器时,可以使用 nginx 的反向代理功能: 用 nginx 在80端口监听所…

    Linux笔记 2018-07-08
  • 计算机的组成及功能(硬件及操作系统)

    主要从计算机的硬件和操作系统两方面介绍了计算机的组成及功能

    2018-05-11
  • Centos6.9 的安装过程

                                                                              Centos6.9虚拟机创建过程 (本次安装过程以VMware14版本为主) 一、            首先需要先获取centos6.9的安装包,如果计算机上还未有centos6.9的安装包,你可以到以下网址进…

    Linux笔记 2018-03-31
  • 脚本基础课后练习

    (1)编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小 #!/bin/bash echo “MY hostname is `hostname`”echo “My IPv4 address is `ifconfig …

    Linux笔记 2018-04-13
  • 基本正则表达式

    城还是这座城,人已非故人

    2018-04-18