簡體   English   中英

將查找命令的輸出追加到Bash腳本中的變量

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM