while read 迴圈在第一行之後停止,即使它沒有從 stdin 讀取

while read 迴圈在第一行之後停止,即使它沒有從 stdin 讀取

我有一個名為test包含的文件

test
test

我正在運行這個命令

while read line
do
echo "$line"
done </tmp/test

這應該輸出“test”兩次,但它只輸出一次。使用while IFS= read -r line不會改變任何東西。解決這個問題的方法是輸出第三個空行,但無論如何它都應該有效。

答案1

嘗試:

while IFS='' read -r line || [ "$line" ]

是的,它正在從標準輸入(stdin)讀取,<test重定向到stdin。

可能發生的情況是 read 被設計用於處理文字檔案。正確的文字檔案必須以換行符號結尾

做一個換行符上的檔案結尾結果非常快:

[ -n "$(tail -c1 file)" ] && printf '\n' >>file 

但是,如果您無法編輯檔案以新增換行符號(如果遺失),則解決方案是(另外)測試是否讀取了某些內容:

while read line || [ "$line" ]

如果讀取失敗(到達檔案末尾而不讀取換行符(預設))但讀取了某些內容(in "$line"),這將使循環執行。

對於以換行符結尾或不以換行符號結尾的文件,這都可以正常工作。

不,aIFS='' read不會(直接)影響最後一行的讀取,事實上,IFS=''(與預設的 IFS 相比)[A]空格、製表符、換行符)只會影響拆分成一些變數(除了影響前導和/或尾隨空白的刪除(如果 IFS 僅包含空白)[b]))。由於只有一個變數 ( "$line"),因此無需執行拆分。

讀取選項-d(自 2.04 起在 bash 中)也無濟於事。沒有特定的“文件結束”字元來匹配(最後一個位元組可以是任何字元)。

剩下的唯一選擇是:

  • 修復文件以使其成為正確的文字文件
  • 測試是否有任何內容被讀入變數line

那麼,正確的腳本將是:

#!/bin/bash
while IFS='' read -r line || [ "$line" ]
do
    printf '%s\n' "$line"
done <test

IFS='' 用於避免 IFS 的不常見值出現問題。


[一][二]當然,如果 IFS 可能有任何價值,那麼許多其他字元可能會被刪除。嘗試(對於以“x”結尾的值)

printf "test\ntesx\ntest" | while IFS="x" read -r line || [ "$line" ]; do echo "$line"; done

不,zsh 做了一些不同的事情。

相關內容