我使用的一个 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
是一个标准命令,当没有给出任何参数时,它只是将其标准输入转储到其标准输出上按原样。