2015-07-19 58 views
1

我想寫一個簡單的函數來查找在Bash中輸入字符串所有新行,這就是我想出了:讀取輸入字符一個字符似乎跳過換行符

find_newlines() { 
    while IFS= read -r -n 1 c; do 
    if [[ "$c" == $'\n' ]]; then 
     echo 'FOUND NEWLINE' 
    fi 
    done 
} 

我以爲我做了我需要的一切。我將IFS設置爲空,我通過了-r標誌,並且我迫使read每次只讀一個字符-n 1。然而,令我沮喪的是,下面什麼也沒做:

test_data="this is a 
test" 

printf "$test_data" | find_newlines 

我沒有收到任何輸出。我正在Mac OS X下進行測試,並且我在Bash版本3.2.57(Apple提供的一個)和4.3.33(通過Homebrew安裝)上運行了此操作。兩者都給出了相同的結果。

爲了在循環中包含換行符,我需要做些什麼?

+0

用'set -vx'(函數內部)打開調試。不能保證解決你的問題,但它可能會給你一些見解。有趣的問題。祝你好運。 – shellter

回答

2

這裏的問題是,read即使在-n 1模式下仍然是讀取分隔線。所以當它看到換行符時,它仍然認爲是一個行分隔符並將其移除(並留下一個空變量)。

$ find_newlines() { 
    while IFS= read -r -n 1 c; do 
     declare -p c; 
    done 
} 
$ printf 'a\nb' | find_newlines 
declare -- c="a" 
declare -- c="" 
declare -- c="b" 

正如在回答賽勒斯使用bash 4.1+ ref可以使用-N標誌read避免這個問題表示。

-N選項被記錄爲(從read Bash的參考手冊項):

-N nchars

read返回看完後正好nchars字符,而不是等待輸入完整系列除非遇到EOF或讀取超時。在輸入中遇到的分隔符字符不會被專門處理,並且不會導致讀取返回,直到讀取nchars字符。

對於bash的非4.1 +版本(2.04+它看起來像)你可以使用-d標誌read指定替代的分隔符來解決此問題。您輸入中不存在的任何分隔符都可以使用。對於許多輸入流而言,最可能的值可能是NUL/\0字符,您可以將其指定爲read-d ''

$ find_newlines() { 
    while IFS= read -d '' -r -n 1 c; do 
     declare -p c; 
    done 
} 
$ printf 'a\nb' | find_newlines 
declare -- c="a" 
declare -- c=" 
" 
declare -- c="b" 
3

-N替換讀取的選項-n

參見:https://unix.stackexchange.com/a/27424/74329

+0

嗯,這似乎工作在Bash 4.3但不是3.2('read'不支持'-N'標誌)。有沒有辦法以與3.x向後兼容的方式做到這一點? –

+1

不適用於bash 3.2.57。對於那個使用'-d'''應該可以工作。問題是閱讀仍然期待分隔符字符和吃掉換行符。因此,使用'-N'告訴'read'不要這樣做和/或改變分隔符將「修復」這個問題。 –

+0

@EtanReisner啊哈,使用'-d'''的作品!我會接受一個包含'-N'和使用'-d'來解決向後兼容性問題的答案,不管它是一個單獨的答案還是隻是編輯到這個答案中(我只是希望它是全面的)。 –

0

也許你可以用sed來替換一些獨特的符號,你可以稍後跟蹤換行字符。這將無視bash版本的工作。

enter code here 

sed的':一個; N; $ BA; S/\ N/@ /克!< < < 「BLA

BLA

BLA」

BLA @ BLA @bla

+0

我寧願不這樣做,因爲它需要在任何輸出發生之前加載整個字符串,而使用'read'則允許輸出逐步發生,這對於非常大的輸入或實時管道更好。不過,這在技術上可以回答這個問題。 –