Bash

逐行列印文件,但在執行過程中處理文件更改

  • October 26, 2017

以下bash命令讀取輸入文件,然後以 15 秒的間隔列印每一行:

IFS=$'\n';for line in $(cat file.txt | tail -n +2); do echo $line && sleep 15; done;

但是,由於每行之間有很長的延遲,我可能會在此命令執行時修改輸入文件

例如,我可能會修改第五行,而該命令只列印了前三行。30 秒後,當命令到達第五行並列印它時,我希望它顯示更新的行,而不是像第一次執行命令時那樣顯示文件。

Example input file.txt

Continents

Africa

Antarctica

Asia

Australia

Europe

N.America

S.America

輸入文件中沒有順序

修改後的輸入file.txt

非洲

南極洲

歐洲

亞洲

澳大利亞

N.America

S.America

每次執行時都必須讀取文件,試試這個:

num=1
while true; do
if [[ $num < $(wc -l file.txt) ]]; then
   awk "NR==$num" file.txt && let "num++"
else
   break
fi
sleep 15
done

這將列印一行,休眠 15 秒然後繼續,當沒有更多行時它將退出。awkNR 是記錄數或行號的內置變數。

使用以下內容並僅附加到文件(或非常小心如何編輯它)

awk '{ system("echo "$0"; sleep 15") }' file.txt

命令

您遇到的主要問題是如何讀取文件$(cat file.txt | tail -n +2)將首先執行 - 讀取整個文件並在循環開始之前生成要循環的行列表。在這一點上,當您完成閱讀文件時,文件會發生什麼並不重要。然後,您使用昂貴的命令遍歷這些行。

相反,您希望應用程序一次讀取和處理一行。您可以使用awksystem命令執行此操作。

awk '{ system("echo "$0"; sleep 15") }' file.txt

這裡我們使用awk打開文件,逐行處理。在每一行上,它將"echo "$0"; sleep 15"在 shell 中執行命令。請注意,引號很重要,$0必須在 sed 引號之外才能將其替換為目前行,否則它將被視為命令的一部分。

修改文件

但是,該命令可能不是您唯一的問題。在後台,大多數應用程序通過寫入一個全新的文件來修改文件,然後在點擊保存時刪除舊文件並重命名新文件。這樣做是為了安全 - 如果他們在寫入文件的中途崩潰,因為原始文件保持完好無損,只有在他們成功寫入文件後,他們才會以非常快速的操作刪除舊文件,使其看起來像文件已就地修改。這意味著 awk(或您可能使用的任何命令)需要關閉並再次打開文件才能開始讀取修改。

不幸的是,大多數修改文件的命令都是這樣做的。但是,您可以安全地附加到文件,例如

echo "Test" >> file.txt

一旦完成所有其他國家/地區,上述命令將開始處理它。

所有這一切實際上都是有充分理由的,你正在掩飾大量的競爭條件和極端情況,如果你需要做的事情很簡單的話,這些情況是可能的。一方面,如果你在你想要的行之前修改一個文件會發生什麼?文件位置由字節位置而不是行號跟踪,因此如果您修改的行長度不同,那麼您的應用程序將失去它在文件中的位置。此外,您還必須考慮,即使應用程序可以就地修改文件,它實際上是從頭開始再次寫入整個文件,而不是僅僅更新已更改的字節(再次由於您添加/移動的行可能不同大小與原件相同)。

或者,您可以每次讀取文件並跟踪您所在的行號 - 這解決了上述一些問題,但仍有一些極端情況需要考慮,例如如果您在目前行之前添加一行會發生什麼情況正在處理?在一個簡單的解決方案中,您將處理目前行兩次,這可能是也可能不是問題。您絕對最好的做法是編寫自己的應用程序來讀取文件,如果它被您修改/刪除,則重新讀取它並手動處理文件中的更改以解決任何差異,但這將涉及更多工作和簡單的追加您的情況可能只需要像上面這樣的方法。

引用自:https://unix.stackexchange.com/questions/400518