一條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參數與不加該參數的區別
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章