如何使用namedpipe作為暫存檔案?

如何使用namedpipe作為暫存檔案?

我使用的一個 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)指定,就像CISO 9899 指定的語法一樣。

該規格,您會注意到read需要一個變數名參數。當你省略它時的行為是未指定並且在實踐中隨sh解釋器的實現而變化。

bash是GNUsh解釋器,就像gccGNU C編譯器一樣。bash和都gcc對這些標準指定的內容進行了擴展。

就 而言read,就當作是 一樣bash對待。在 POSIX 規格中,讀取 stdin 直到到達字元或輸入末尾,並將讀取的字元儲存到變數中並返回read -rIFS= read -r REPLYIFS= read -r REPLY\n$REPLY成功如果讀取了換行符號(整行)或則退出狀態失敗否則(如換行符之前的 EOF),並且如果讀取的資料包含 NUL 字元或不形成有效字元的位元組序列,則行為未定義。

在 的情況下bash,即使讀取的位元組不構成有效字符,它也會儲存它們並刪除 NUL 字符。

read -r類似於read -r REPLYin kshor並在基於or 的類 POSIX shellzsh中報告錯誤。yashash

的行為echo是未指定的,除非它的參數不包含反斜線字元且第一個不是-n

因此,總而言之,除非您知道sh正在處理的特定實作(和版本),否則您無法分辨什麼

while read -r; do
  echo "$REPLY" >> "$temp_file"
done

會做。在具體情況下bash,僅當資料不包含 NUL 字元、以換行符結尾且沒有任何行與^-[neE]+$擴展正則表達式匹配(和/或取決於環境或如何像 OS/Xbash一樣編譯sh,不包含反斜線字元) 。

這也是效率非常低,而且不是你在 shell 中處理文字的方式

在這裡,您想要:

cat > "$temp_file"

cat是一個標準命令,當沒有給出任何參數時,它只是將其標準輸入轉儲到其標準輸出上按原樣

相關內容