
在下面的腳本中,第一個為了循環按預期執行,但不是第二個。我沒有收到任何錯誤,似乎腳本只是掛起。
HOME=/root/mydir
DIR=$HOME/var
DIRWORK=$HOME/Local
for f in $(find $DIR -type f); do
lsof -n $f | grep [a-z] > /dev/null
if [ $? != 0 ]; then
echo "hi"
fi
done
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
答案1
您的腳本以危險的方式編碼。
首先,我假設您正在使用 Bash shell,因為您將其標記為“/bash”和“/for”。
在我的回答中我會引用這個偉大的重擊指南,這可能是學習 Bash 的最佳來源。
1)切勿使用命令替換, 的任何一個親切,不帶引號。這裡有一個主要問題:使用不含引號的擴充將輸出拆分為參數。
具體來說,這$(find $DIRWORK -type d -name work)
將$(find $DIR -type f)
經歷分詞,因此如果find
發現一個檔案名稱中包含空格,即“檔案名稱”,Bash 的分詞結果將傳遞 2 個參數供命令for
迭代,即一個用於“檔案”,一個用於“名稱”。在這種情況下,您希望獲得“檔案:沒有這樣的檔案或目錄”和“名稱:沒有這樣的檔案或目錄”,而不是如果它們確實存在的話可能會對它們造成損壞。
2)依照慣例,環境變數(PATH、EDITOR、SHELL...)和內部 shell 變數(BASH_VERSION、RANDOM...)都是完全大寫的。所有其他變數名稱都應小寫。由於變數名稱區分大小寫,因此此約定可以避免意外覆蓋環境變數和內部變數。
您的 $DIRWORK 目錄打破了該約定,並且它也未加引號,因此如果我們 let DIRWORK='/path/to/dir1 /path/to/dir2'
,find
當 $DIRWORK 未加引號時,將查看兩個不同的目錄。使用引號的主題在 Bash 中非常重要,因此您應該“雙引號”每一個擴展,以及任何可能包含特殊字元的內容,例如“$var”、“$@”、“${array[@]}”、“$(command)”。 Bash 將「單引號」內的所有內容視為字面意思。了解 ' 和 " 和 ` 之間的差異。請參閱引號,論據您可能還想看看這個連結:http://wiki.bash-hackers.org/syntax/words
這是腳本的更安全版本,我建議您改用:
my_home="/root/mydir"
my_dir="$my_home/var"
dir_work="$my_home/Local"
while IFS= read -r -d '' f; do
# I'm guessing that you also want to ignore stderr;
# this is where the 2>&1 came from.
if lsof -n "$f" | grep '[a-z]' > /dev/null 2>&1; then
echo "hey, I'm safer now!"
fi
done < <(find "$dir_work" -type f -print0)
while IFS= read -r -d '' f; do
echo "2"
done < <(find "$dir_work" -type d -name 'work' -print0)
正如您所看到的,該IFS
變數被設定為空,從而防止read
修剪行中的前導和尾隨空格。此read
指令使用空字串 ( -d ''
) 作為分隔符,讀取直到到達 \0。
find
需要進行相應的修改,因此它使用-print0
選項來用 \0 而不是新行來分隔其資料 - 令人驚訝且惡意的是,它可以是檔案名稱的一部分。將這樣的文件按 \n 分成兩部分將會破壞我們的程式碼。
您可能想閱讀有關流程替代如果你不完全理解我的腳本。
先前的答案指出應該find ... | while read name; do ...; done
用於讀取find
輸出也可能很糟糕。上面的循環while
在一個新的子 shell 中執行,該子 shell 具有從父 shell 複製的變數副本。然後,該副本可用於您喜歡的任何用途。當while
循環結束時,子 shell 副本被丟棄,父 shell 的原始變數沒有改變。
如果您的目標是修改此while
循環內的某些變數並隨後在父級中使用它們,請考慮使用上面更安全的腳本,這將防止資料遺失。
答案2
這段程式碼
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
將會先執行這一行
find $DIRWORK -type d -name work
等待find
執行完成,然後取得輸出並將其放回for
循環中
for i in the output of find; do
echo "2"
done
只有這樣 for 迴圈才會開始執行。
因此,如果find
需要很長時間才能完成for
循環,則必須等待很長時間才能開始。
嘗試find
在互動式提示中計時命令
$ time find $DIRWORK -type d -name work
看看需要多長時間。
另請注意:不應使用for
循環來循環檔案名稱。使用這樣的while
循環:read
find $DIRWORK -type d -name work | while read name; do
echo "2"
done
讀這了解更多。
獎勵:這while
與 並行執行循環find
。這意味著循環將在列印出一行while
後立即執行一次迭代。find
它不必等待find
完成執行。