
我使用的一個 vim 插件使用此腳本將一些輸入傳遞給不支援從 stdin 讀取的 linter。
set -eu
# All of the following arguments are read as command to run.
file_extension="$1"
shift
temp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'ale_linter')
temp_file="$temp_dir/file$file_extension"
trap 'rm -r "$temp_dir"' EXIT
while read -r; do
echo "$REPLY" >> "$temp_file"
done
"$@" "$temp_file"
起初我有點困惑為什麼他們不直接使用類似的東西
some input | some_program /dev/stdin
但是在嘗試ghc
作為 linter 之後,我發現它在抱怨 /dev/stdin 說它不是真正的檔案(它不是)
所以我想知道我可以使用命名管道而不是臨時檔案。我對寫入臨時文件不太滿意的原因是 SSD 的健康狀況,如果有更好的方法來做到這一點為什麼不這樣做,對吧?
答案1
不,拒絕這些文件的程序通常會拒絕它們,因為該文件不是可尋找的(他們需要以任意偏移量存取內容,或在倒帶後多次訪問等)。或者他們想要多次打開該文件。他們可能還想重寫(部分)文件或截斷它。
未命名pipe
(如|
和/dev/stdin
)或命名在任何這些情況下都沒有區別。
實際上,在 Linux 上,/dev/stdin
當 stdin 是管道(命名或未命名)時,其行為與命名管道完全相同,程式將無法將其/dev/stdin
與真正的命名管道區分開。
在其他系統上,它並不完全相同,但實際上,開啟/dev/stdin
或命名管道將為您提供管道的檔案描述符,這是無論如何都不可找到的。
因此,您需要建立臨時文件。請注意,有些 shell 可以讓這件事變得更容易。對於zsh
,它只是:
#! /bin/zsh -
"$@" =(cat)
在 Linux 上以及使用已刪除的臨時檔案作為此處文件的 shell(例如bash
以及zsh
的一些實作ksh
),您可以執行以下操作:
#! /bin/bash -
"$@" /dev/fd/3 3<< EOF
$(cat)
EOF
但是,如果檔案包含 NUL 字元或以空白行結尾,則可能會破壞檔案的內容。
請注意,從版本 5 開始,bash 將此處的 doc 暫存檔案設為唯讀,因此如果應用程式需要對該檔案進行修改,您將使用下列命令還原寫入權限:
#! /bin/bash -
{
chmod u+w /dev/fd/3 && # only needed in bash 5+
"$@" /dev/fd/3
} 3<< EOF
$(cat)
EOF
while read
自您詢問以來有關該循環的註釋。
首先read -r
沒有變數名是無效的sh
語法。此sh
語法由 POSIX(ISO 9945,也是 IEEE Std 1003.1)指定,就像C
ISO 9899 指定的語法一樣。
在該規格,您會注意到read
需要一個變數名參數。當你省略它時的行為是未指定並且在實踐中隨sh
解釋器的實現而變化。
bash
是GNUsh
解釋器,就像gcc
GNU C編譯器一樣。bash
和都gcc
對這些標準指定的內容進行了擴展。
就 而言read
,就當作是 一樣bash
對待。在 POSIX 規格中,讀取 stdin 直到到達字元或輸入末尾,並將讀取的字元儲存到變數中並返回read -r
IFS= read -r REPLY
IFS= read -r REPLY
\n
$REPLY
成功如果讀取了換行符號(整行)或則退出狀態失敗否則(如換行符之前的 EOF),並且如果讀取的資料包含 NUL 字元或不形成有效字元的位元組序列,則行為未定義。
在 的情況下bash
,即使讀取的位元組不構成有效字符,它也會儲存它們並刪除 NUL 字符。
read -r
類似於read -r REPLY
in ksh
or並在基於or 的類 POSIX shellzsh
中報告錯誤。yash
ash
的行為echo
是未指定的,除非它的參數不包含反斜線字元且第一個不是-n
。
因此,總而言之,除非您知道sh
正在處理的特定實作(和版本),否則您無法分辨什麼
while read -r; do
echo "$REPLY" >> "$temp_file"
done
會做。在具體情況下bash
,僅當資料不包含 NUL 字元、以換行符結尾且沒有任何行與^-[neE]+$
擴展正則表達式匹配(和/或取決於環境或如何像 OS/Xbash
一樣編譯sh
,不包含反斜線字元) 。
這也是效率非常低,而且不是你在 shell 中處理文字的方式。
在這裡,您想要:
cat > "$temp_file"
cat
是一個標準命令,當沒有給出任何參數時,它只是將其標準輸入轉儲到其標準輸出上按原樣。