目 錄
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參數與不加該參數的區別