Bash

當我使用空字元串作為參數時,“read”shell 命令的“-d”選項有什麼作用?

  • May 7, 2022

當我閱讀這個答案時,作者使用這個命令將heredoc的結果放到一個變數中:

read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

我對這個-d選項有點困惑。從read命令的幫助文本中:

-d delim
continue until the first character of DELIM is read, rather than newline

因此,如果我將一個空字元串傳遞給-d,則意味著讀取到第一個空字元串。這是什麼意思?作者在答案下發表評論,這-d ''意味著使用 NUL 字元串作為分隔符。這是真的嗎(空字元串表示 NUL 字元串)?為什麼不使用之類的東西-d '\0'-d '\x0'

大多數情況下,它的意思是它所說的,例如:

$ 閱讀 -d 。變種;迴聲; 迴聲“讀取:'$var'”
富。
閱讀:“富”

閱讀立即在 處結束.,我沒有在此處輸入。

但是read -d ''有點特殊,線上參考手冊說

-d delim delim

的第一個字元用於終止輸入行,而不是換行符。如果 delim 是空字元串,read 將在讀取 NUL 字元時終止一行。

\0表示 中的 NUL 字節printf,所以我們有例如:

$ printf 'foo\0bar\0' | while read -d '' var; do echo "read: '$var'"; done
read: 'foo'
read: 'bar'

在您的範例中,read -d ''用於防止換行符成為分隔符,允許它一次性讀取多行字元串,而不是一次讀取一行。


我認為一些舊版本的文件沒有明確提到-d ''. 這種行為最初可能是與 Bash 如何以 C 方式儲存字元串以及尾隨 NUL 字節的意外巧合。字元串foo儲存為foo\0,空字元串儲存為 just \0。因此,如果實現不小心防範它並且只選擇記憶體中的第一個字節,它將看到\0NUL 作為空字元串的第一個字節。

更仔細地重新閱讀這個問題,你提到:

作者在答案下發表評論,這-d ''意味著使用NUL 字元串作為分隔符。

這並不完全正確。空字元串(在 POSIX 用語中)表示長度為零的空字元串,即不包含任何內容的字元串。這與NUL 字節不同,後者是二進制值為零(*)的單個字節。如果你使用空字元串作為分隔符,你會發現它幾乎無處不在,在每個可能的位置。我認為這在 shell 中是不可能的,但例如在 Perl 中,可以像這樣拆分字元串,例如:

$ perl -le 'print join ":", split "", "foobar";'
f:o:o:b:a:r

read -d ''使用 NUL字節作為分隔符。

當然與字元* 0不同。)

為什麼不使用之類的東西-d '\0'-d '\x0'

嗯,這是個好問題。正如 Stéphane 評論的那樣,最初,ksh93read -d不支持read -d ''這樣的,並且將其更改為支持反斜杠轉義將與原始版本不兼容。但是,如果您更喜歡它,您仍然可以使用read -d $'\0'(同樣$'\t'用於選項卡等)。只是在幕後,這與 相同-d '',因為 Bash 不支持字元串中的 NUL 字節。Zsh 可以,但它似乎同時接受-d ''-d $'\0'

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