一条linux批量拷贝命令引发的分析和思考

0.需求分析

    在项目部署初始化之前往往需要将某个公共文件夹(如配置参数文件、UDF函数等等)拷贝复制到linux各个子文件夹下,一种方式是提前将公共文件放置到各个子文件夹下,一种是采用cp命令挨着拷贝写到某个脚本中,但是随着项目增大,代码文件等越来越多导致上述方式比较机械,也不便于代码维护,本文将介绍一种自动化实现方式,避免代码冗余,便于维护。

1.脚本实现

  (1)目标

          如上图所示,我们需要将JTTL_ETL_COMMON中各个文件夹拷贝到指定的各个子文件夹中,子文件夹的目录深度为2。

  (2)具体实现

           为了保证幂等性,我们先删后复制,具体脚本实现如下

#!/bin/bash
#将input.sh脚本放到二级文件夹下,先删,后复制
find /home/centos/phm/ -path /home/centos/phm/JTTL_ETL_COMMON -prune -o -name "input.sh" -print | xargs rm -rf
find /home/centos/phm/ -maxdepth 2 -mindepth 2 -type d | xargs -i find {} -path "/home/centos/phm/SUB_MAIN_WF/*" -prune -o -type d -print | xargs -i cp -r /home/centos/phm/JTTL_ETL_COMMON/"input.sh" {}

2.脚本分析

    如上图所示,我们需要将JTTL_ETL_COMMON中各个文件夹拷贝到上述各个子文件夹中,观察各个子文件夹,发现目录的深度为2,也就是说需要将公共文件拷贝到二级目下。那么如何实现公共文件拷贝到所有的子文件夹下呢?我们可以利用linux find命令

    采用find的maxdepth和mindepth限定目录层级,-d参数限定为目录与是我们看一下结果

[root@bigdata3 ~]# find /home/centos/phm/ -maxdepth 2 -mindepth 2 -type d -print

   得到如图所示结果:找到二级目录

 接着我们采用管道命令利用cp命令将公共文件下的文件拷贝到各个子文件

[root@bigdata3 ~]# find /home/centos/phm/ -maxdepth 2 -mindepth 2 -type d -print | cp -r /home/centos/phm/JTTL_ETL_COMMON/"input.sh"

报如下错误:

    错误的原因是cp拷贝的目标地址需要的是以参数的形式传入,而不是以流的形式(文件内容)的形式传入,这点一定需要注意区别。

   下面我们着重对这以问题进行分析:

[root@bigdata3 ~]# echo "--help" | cat 

[root@bigdata3 ~]# echo "--help" |xargs cat

    对比上述两种方式我们可以看到 echo "--help" | cat相当于将"--help"作为cat 的输入内容,所以输出到屏幕将原样内容输出,而echo "--help" |xargs cat,相当于将--help作为cat的参数等价于cat --help,因而输出的是cat的帮助文档。

再聊find与cp:

    cp命令主要用于复制文件或目录,可以将一个或多个源文件或者目录复制到指定的目的文件或目录,当一次复制多个文件时,目标文件参数必须是一个已经存在的目录,否则将出现错误。cp命令支持将多个文件复制到指定的目标文件或目录,但不支持将文件复制到多个目标文件或目录。如下图所示

    因而cp命令后面只接受一个参数,find命令将找到所有参数全部给cp作为目标地址 ,故而报错,为了解决上述问题我们使用xargs -i参数。

    xargs 的一个选项 -i ,使用 -i指定一个替换字符串 {},这个字符串在 xargs 扩展时会被替换掉,当 -i与 xargs 结合使用,每一个参数命令都会被执行一次,也就是说指定-I参数后,{}代替了输入内容,里面的每一个内容能够循环按照要求替换相应的参数,即参数是一条一条传给后面的指令的.

    注意:使用-i参数还是-I参数看linux的版本支持。

-i 和{} 的作用?

    xargs 的-i参数 表示 find 传递给xargs的结果 由{}来代替,并将{}里面的结果一条一条传给后面要执行的命令作为参数。{}既可以作为源地址也可以作为目标地址,可以理解{}为一个占位符。

如下列子所示:

[root@bigdata3 ~]# find . -type d | xargs -i ls -ltr {}

      去掉-i参数结果如图所示:

[root@bigdata3 ~]# find . -type d | xargs ls -ltr 

     查看结果如下:

    此验证说明了ls后面是可以跟多个目标地址,类似的rm命令后面也支持一次删除多个目标文件,这里不做验证,读者可自行验证。
那么如何实现指定文件夹的复制删除,而忽略某些目录呢?

     这里采用find 的prune参数

    -prune用法很严格,若要忽略某个目录一般采用如下固定模式:

     find 查找文件的目录 -path 需要排除的目录 -prune -o -name 需要查询的内容

     注意事项:理解为固定用法就可以了(path->prune->o->print)忽略四部曲

  • 1)-prune 必须和 -path, -o 一起使用
  • 2)-prune -o 的顺序不 能调换
  • 3)-name等必须放在-prune -o后面才能使用
  • 4)如果后面有管道符号前面需要加-print参数

例如:

find /home/centos/phm/ -path /home/centos/phm/JTTL_ETL_COMMON -prune -o -name "input.sh" -print | xargs rm -rf
伪代码解释:

if -path "/home/centos/phm/JTTL_ETL_COMMON" then

      -prune(忽略该目录)

else

      -print(打印满足条件后找到的内容)。

    当然上述find cp命令我们也可以用find exec参数来实现如下命令所示:

find /home/centos/phm/ -maxdepth 2 -mindepth 2 -type d -exec cp -r /home/centos/phm/JTTL_ETL_COMMON/"input.sh" {} \;

    但上述命令存在的隐患是参数过多会有溢出的危险,因而我们还是选用下面一种比较安全

find /home/centos/phm/ -maxdepth 2 -mindepth 2 -type d  |xargs -i  cp -r /home/centos/phm/JTTL_ETL_COMMON/"input.sh" {}

   对于find -exec参数中{}命令和xargs -i命令中{}理解是一致的,对于该参数的使用需要注意一点结尾是\结尾且与前面有个空格,空格特别要注意被忽略,否则命令会出错,一般不建议使用-exec参数,另外需要注意{}的灵活运用,下面例子说明这一点

find /home/centos/phm/ -maxdepth 2 -mindepth 2 -type d -exec rm -rf  {}/input.sh \;

    表示删除找到二级目录下的input.sh文件,等价于下面这条命令

find /home/centos/phm/ -name "input.sh" -exec rm -rf  {} \;

3. 小结

   本文主要探讨了以下内容,通过本文的学习你将获得以下知识:

  • (1)文件批量复制删除的方法;
  • (2)linux find命令基本用法
  • (3)xargs -i参数的使用及与find命令结合的使用
  • (4)find 命令忽略某个目录的用法
  • (5)管道命令|加xargs参数与不加该参数的区别
发布了11 篇原创文章 · 获赞 165 · 访问量 5375

猜你喜欢

转载自blog.csdn.net/godlovedaniel/article/details/104557834