我在 shell 腳本中有以下內容:
for file in $local_dir/myfile.log.*;
do
file_name=$(basename $file);
server_name=$(echo $file_name | cut -f 3 -d '.');
file_location=$(echo $file);
mv $file_location $local_dir/in_progress1.log
mysql -hxxx -P3306 -uxxx -pxxx -e "set @server_name='${server_name}'; source ${sql_script};"
rm $local_dir/in_progress1.log
done
它基本上會取得目錄中符合條件的所有文件,從文件名中提取伺服器名,然後將其傳遞給 MySQL 腳本進行處理。
我想知道是否有 10 個文件,每個文件需要 60 秒才能完成,5 分鐘後我啟動 shell 腳本的第二個實例:
- a) 第二個腳本是否仍會看到尚未處理的文件
- b) 如果刪除文件,第一次會導致問題嗎
或者我可以毫無問題地並行運行它們嗎?
答案1
人們會認為「60秒」(甚至「5分鐘」)只是一個不錯的估計,並且存在當第二批開始時第一批仍在進行中的風險。如果您想分隔批次(並且除了偶爾重疊的日誌檔案之外沒有問題),更好的方法是將批次號碼作為正在進行的檔案命名約定的一部分。
像這樣的東西:
[[ -s ]] $local_dir/batch || echo 0 > $local_dir/batch
batch=$(echo $local_dir/batch)
expr $batch + 1 >$local_dir/batch
在 for 循環之前,然後在循環開始時,檢查您的模式是否與實際文件匹配
[[ -f "$file" ]] || continue
並在檔案名稱中使用批號:
mv $file_location $local_dir/in_progress$batch.log
往復。這降低了碰撞的風險。
答案2
上面有一個答案為該問題提供了一些很好的解決方案,但我想我應該提供一些解釋為什麼問題是什麼。
大多數情況下:只要您重新命名的日誌檔案(正在進行的日誌檔案)不符合標準,您就可以大概可以安全地運行它最小的風險。但你還是會遇到一些錯誤...
您的文件清單是在腳本執行時產生的。所以最終會發生的是:
Script A
取得 的列表10 files
。開始處理,5 files
在(剩餘5)中script B
得到一個列表5 remaining files
,開始處理。Script a
然後去處理其列表中的下一個文件(與文件已開始處理的相同script B
),它將出錯,因為文件已被重新命名。因此,透過錯誤處理,理論上它可以毫無問題地轉到其列表和函數中的下一個。但是,顯然總是有機會對齊星星,但腳本同時擊中同一個文件,並且會發生意想不到的事情。隨意權衡風險。
一種可能更優雅的解決方案是將其轉換為python
腳本,並研究parallel for loops
它允許您創建單個 for 循環,並並行運行它,從而允許一個腳本完成兩個或多個腳本的工作。
答案3
另一種方法是在腳本中實作一個簡單的批次佇列。
在腳本的開頭,您可以執行以下操作:
mkdir -p $localdir/batch
BATCHTMP=$(mktemp batch.XXXXXXXXXX)
MYBATCH="$localdir/batch/batch.$$"
# get list of current log files
find $local_dir/ -name 'myfile.log.*' > "$BATCHTMP"
# exclude any log files already in other batches
grep -vF -f <(sort -u $localdir/batch/batch.*) < "$BATCHTMP" > "$MYBATCH"
rm -f "$BATCHTMP"
# only process log files that are in my batch
for lf in $(cat "$MYBATCH") ; do
....
# somewhere in here, mv or rm the logfile being processed
# so it doesn't get processed again in a later batch run
done
rm -f "$MYBATCH"
當然,這只是需要做的事情的簡單概述。
順便說一句,這也可以在包裝腳本中完成,該腳本除了生成批次文件然後運行主腳本之外什麼也不做。