[英]Append output of Find command to Variable in Bash Script
嘗試將find命令的輸出附加到Bash腳本中的變量
可以將find命令的輸出附加到日志文件中,但是不能將其附加到變量即
這樣可以正常工作:
find $DIR -type d -name "*" >> $DIRS_REMOVED_LOG
但這不會:
FILES_TO_EVAL=find $DIR -type f \( -name '*.sh' -or -name '*.txt' -or -name '*.xml' -or -name '*.log' \)
ENV=`basename $PS_CFG_HOME | tr "[:lower:]" "[:upper:]"`
FILE_TYPES=(*.log *.xml *.txt *.sh)
DIRS_TO_CLEAR="$PS_CFG_HOME/data/files $PS_CFG_HOME/appserv/prcs/$ENV/files $PS_CFG_HOME/appserv/prcs/$ENV/files/CQ"
FILES_REMOVED_LOG=$PS_CFG_HOME/files_removed.log
DIRS_REMOVED_LOG=$PS_CFG_HOME/dirs_removed.log
##Cycle through directories
##Below for files_removed_log works ok but can't get the find into a variable.
for DIR in `echo $DIRS_TO_CLEAR`
do
echo "Searching $DIR for files:"
FILES_TO_EVAL=find $DIR -type f \( -name '*.sh' -or -name '*.txt' -or -name '*.xml' -or -name '*.log' \)
find $DIR -type d -name "*" >> $DIRS_REMOVED_LOG
done
預期將使用find
命令的結果填充FILES_TO_EVAL
,但它為空。
通過ShellCheck運行腳本。 它會發現許多常見錯誤,就像編譯器會發現的那樣。
FILES_TO_EVAL=find $DIR -type f \( -name '*.sh' -or -name '*.txt' -or -name '*.xml' -or -name '*.log' \)
SC2209:使用
var=$(command)
分配輸出(或引號分配字符串)。
除了shellcheck.net會指出的問題外,還有許多微妙的問題。
一方面,您正在使用全大寫字母的變量名。 這很危險,因為有很多全大寫變量對Shell和/或其他工具具有特殊含義,並且如果您不小心使用其中的一種,可能會產生奇怪的效果。 小寫或混合大小寫的變量更加安全(除非您特別想要特殊含義)。
另外,幾乎應該始終在變量引用周圍加上雙引號(例如, find "$dir" ...
而不是find $dir ...
)。 沒有它們,變量將受到單詞拆分和通配符擴展的影響,這可能會帶來各種意外的后果。 在某些情況下,您需要對變量的值進行字分割和/或通配符擴展,但通常不完全是Shell的方式; 在這種情況下,您應該尋找一種更好的方法來完成這項工作。
在失敗的那一行,
FILES_TO_EVAL=find $DIR -type f \( -name '*.sh' -or -name '*.txt' -or -name '*.xml' -or -name '*.log' \)
直接的問題是您需要使用$(find ...)
來捕獲find
命令的輸出。 但這仍然很危險,因為它只是存儲以換行符分隔的文件路徑列表,並且擴展此文件的標准方法(僅使用未引用的變量引用)具有我上面提到的所有問題。 在這種情況下,如果任何文件名包含空格或通配符(這在文件名中完全合法),將導致麻煩。 在處於可控環境中的環境中,您可以保證不會發生這種情況,但是您會擺脫它的束縛……但這實際上不是最好的主意。
從find
正確處理文件路徑列表有些復雜,但是有很多方法可以做到。 BashFAQ#20中有很多很好的信息:“如何找到並安全地處理包含換行符,空格或兩者兼有的文件名?” 我將在下面總結一些常見的選項:
如果不需要存儲列表,只需在單個文件上運行命令,則可以使用find -exec
:
find "$dir" -type f \( -name '*.sh' -or -name '*.txt' -or -name '*.xml' -or -name '*.log' \) -exec somecommand {} \;
如果需要運行更復雜的內容,可以使用find -print0
以明確的形式輸出列表,然后使用read -d ''
讀取它們。 這里有很多潛在的陷阱,所以這是我用來避免所有麻煩點的版本:
while IFS= read -r -d '' filepath <&3; do
dosomethingwith "$filepath"
done 3< <(find "$dir" -type f \( -name '*.sh' -or -name '*.txt' -or -name '*.xml' -or -name '*.log' \) -print0)
請注意, <(command)
語法(稱為進程替換)是僅bash的功能,因此請在腳本上使用顯式的bash shebang( #!/bin/bash
或#!/usr/bin/env bash
),以及不要通過使用sh
運行腳本來覆蓋它。
如果確實需要存儲路徑列表以備后用,請將其存儲為數組:
files_to_eval=()
while IFS= read -r -d '' filepath; do
files_to_eval+=("$filepath")
done < <(find "$dir" -type f \( -name '*.sh' -or -name '*.txt' -or -name '*.xml' -or -name '*.log' \) -print0)
..或者,如果您使用的是bash v4.4或更高版本,則使用readarray
(aka mapfile
)會更容易:
readarray -td '' files_to_eval < <(find "$dir" -type f \( -name '*.sh' -or -name '*.txt' -or -name '*.xml' -or -name '*.log' \) -print0)
無論哪種情況,都應使用"${files_to_eval[@]}"
擴展數組,以獲取所有元素,而無需對其進行分詞和通配符擴展。
關於其他一些問題。 在這一行:
FILE_TYPES=(*.log *.xml *.txt *.sh)
在這種情況下,通配符將立即擴展到當前導演中的匹配項列表。 您應該引用它們來防止這種情況:
file_types=("*.log" "*.xml" "*.txt" "*.sh")
在這些行中:
DIRS_TO_CLEAR="$PS_CFG_HOME/data/files $PS_CFG_HOME/appserv/prcs/$ENV/files $PS_CFG_HOME/appserv/prcs/$ENV/files/CQ"
...
for DIR in `echo $DIRS_TO_CLEAR`
您正在將列表存儲為單個字符串,並且條目之間用空格分隔,這會遇到我一直在討論的所有單詞拆分和通配符問題。 同樣,這里的echo
是一個復雜的事情,沒有做任何有用的事情,實際上使通配符問題變得更糟。 使用數組,避免所有混亂:
dirs_to_clear=("$ps_cfg_home/data/files" "$ps_cfg_home/appserv/prcs/$env/files" "$ps_cfg_home/appserv/prcs/$env/files/CQ")
...
for dir in "${dirs_to_clear[@]}"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.