如何使用命名管道作为临时文件?

use*_*095 5 pipe shell-script

我使用的一个 vim 插件使用此脚本将一些输入传递给不支持从标准输入读取的短绒。

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"
Run Code Online (Sandbox Code Playgroud)

起初我有点困惑为什么他们不只是使用这样的东西

some input | some_program /dev/stdin
Run Code Online (Sandbox Code Playgroud)

但是在尝试ghc作为 linter之后,我发现它在抱怨 /dev/stdin 说它不是真实文件(它不是)

所以我想知道我可以使用 namedpipe 而不是临时文件。我对写入临时文件不太满意的原因是 SSD 的健康状况,如果有更好的方法,为什么不这样做,对吧?

Sté*_*las 6

不,那拒绝这些文件的程序通常拒绝他们,理由是该文件不是可查找(他们需要倒带等后,在任意的偏移量,或多次访问内容)。或者他们想多次打开文件。他们可能还想重写(部分)文件或截断它。

未命名pipe(如 with|/dev/stdin)或命名的在任何这些情况下都没有区别。

实际上,在 Linux 上,/dev/stdin当 stdin 是管道(命名或未命名)时,其行为与命名管道完全一样,程序将无法将其/dev/stdin与真正的命名管道区分开来。

在其他系统上,它并不完全相同,但实际上,打开/dev/stdin或命名管道将为您提供管道的文件描述符,这两种方式都无法找到。

因此,您需要创建临时文件。请注意,某些 shell 使其更容易。使用zsh,它只是:

#! /bin/zsh -
"$@" =(cat)
Run Code Online (Sandbox Code Playgroud)

在Linux和与使用已删除临时文件,这里的文件(如贝壳bashzsh和一些实现ksh),你可以这样做:

#! /bin/bash -
"$@" /dev/fd/3 3<< EOF
$(cat)
EOF
Run Code Online (Sandbox Code Playgroud)

但是,如果文件包含 NUL 字符或以空行结尾,则可能会破坏文件的内容。

请注意,从版本 5 开始,bash 将 here doc 临时文件设为只读,因此如果应用程序需要对该文件进行修改,您将使用以下命令恢复写入权限:

#! /bin/bash -
{
  chmod u+w /dev/fd/3 && # only needed in bash 5+
    "$@" /dev/fd/3
} 3<< EOF
$(cat)
EOF
Run Code Online (Sandbox Code Playgroud)

while read自从你问起关于那个循环的注释。

首先read -r没有变量名是无效的sh语法。该sh语法被指定POSIX(ISO 9945,也IEEE标准1003.1)之类的C语法由ISO 9899指定。

该规范中,您会注意到它read需要一个变量名参数。省略它时的行为是未指定的,并且在实践中随sh解释器实现而变化。

bash是 GNUsh解释器,就像gccGNU C 编译器一样。双方bashgcc有超过什么这些标准指定的扩展。

在 的情况下readbashread -r视为IFS= read -r REPLY。在 POSIX 规范中,IFS= read -r REPLY读取 stdin 直到\n到达字符或输入的结尾并将读取的字符存储到$REPLY变量中,如果读取换行符(整行)或失败(如 EOF )则返回成功退出状态在换行符之前),并且如果读取的数据包含 NUL 字符或不构成有效字符的字节序列,则行为未定义。

在 的情况下bash,即使它们不形成有效字符,它也会存储读取的字节并删除 NUL 字符。

read -r就像read -r REPLYkshor中一样,zsh并在基于yashorash的类 POSIX 外壳中报告错误。

的行为echo是未指定的,除非它的参数不包含反斜杠字符并且第一个不是-n

所以,总而言之,除非您知道sh您正在处理的特定实现(和版本),否则您无法分辨

while read -r; do
  echo "$REPLY" >> "$temp_file"
done
Run Code Online (Sandbox Code Playgroud)

会做。在bash具体情况下,它只会将 stdin 存储到 temp_file 中,只要数据不包含 NUL 字符、以换行符结尾并且没有任何行与^-[neE]+$扩展正则表达式匹配(和/或取决于环境或如何bashshOS/X一样编译,不包含反斜杠字符)。

它也非常低效,而不是您在 shells 中处理文本的方式

在这里,你想要:

cat > "$temp_file"
Run Code Online (Sandbox Code Playgroud)

cat是一个标准命令,当没有给出任何参数时,它只是将其标准输入按原样转储到标准输出