如何编写可以从 stdout 获取输入的脚本

如何编写可以从 stdout 获取输入的脚本

我希望能够编写一个可以stdout作为参数的脚本,如果有任何内容通过管道传输到其中(最终,我希望它是多态的)-

问题是,我搜索了又搜索如何做到这一点却无济于事 - 很多关于如何做其他事情的替代建议不是 - 这:

cat /var/log/some.log | grep something | awk '{print $1 $6 $8}' | myscript

为什么要这样做,而不是? :myscript $(!!) 此时此刻,只是为了证明这是可能的......

我知道您可以在脚本中“读取变量”,但假设我不关心这些行 - 比方说,我想接受整个脚本作为文本块并在脚本中对其执行某些操作 -

我真的必须:

while read x; do
stdin=$stdin" "$x;
done;

只是为了从 STDIN 读取?

一定会有更好的办法 ...

答案1

如果您想将所有 stdin 读入 shell 脚本,通常只需将其捕获到临时文件中:

TMPFILE=$(mktemp -- "${TMPDIR:-/tmp}/${0##*/}.$$.XXXXXX") || exit
cat > "$TMPFILE"
# Script works with $TMPFILE and its contents,
# ultimately writing everything to stdout.
rm -f -- "$TMPFILE"

甚至系统实用程序也做类似的事情。sort例如,必须拥有所有标准输入才能将任何内容打印到标准输出。

答案2

您可以通过重定向文件描述符 1 的输入来读取 stdout。根据定义,Stdout 是文件描述符 1。文件描述符 1 用于输出这一事实是惯例问题,而不是技术义务。然而,这是一件奇怪的事情,必然会让使用你的脚本的人感到困惑。

read line <&1

如果要读取整个文件,请使用cat。要将内容填充到变量中,请使用命令替换。

whole_input=$(cat)
whole_input_from_stdout=$(cat <&1)

有些 shell 允许您将其编写$(</dev/stdin)为稍微更有效的快捷方式$(cat)

请注意,这会删除尾随换行符。此行为内置于命令替换中。要保留尾随换行符,请将它们隐藏在另一个字符后面并删除该字符。

whole_input=$(cat; echo .); whole_input=${whole_input%?}

如果输入中存在空字节,则 shell 变量将仅包含第一个空字节之前的数据。使用某些 shell,您将获得去除空字节的所有数据。 shell 无法处理二进制数据。 Zsh 是一个例外,它保留空字节。

答案3

我认为您的意思是您想要编写脚本,以便它可以从其标准输入或参数中处理命令的标准输出。

然后,最明显的方法是检查参数的数量。

如果您的脚本在 shell 变量中处理其输入:

#! /bin/sh -

if [ "$#" -gt 0 ]; then
  # arguments joined with space, the first character of $IFS
  input="$*"
else
  input=$(cat) # stdin without the trailing newline characters
fi

printf 'I got "%s"\n' "$input"

或者,如果未传递任何参数,您可以将 stdin 的每个非空行读入位置参数($@: $1, ...):$2

#! /bin/sh -
NL='
'
if [ "$#" -eq 0 ]; then
  set -o noglob
  IFS=$NL
  set -- $(cat) # split+glob with splitting on NL and glob disabled
fi

echo "I got $# arguments:"
[ "$#" -eq 0 ] || printf ' - "%s"\n' "$@"

或者,如果它将输入作为流处理:

#! /bin/sh -
main() {
  # process standard input as a stream 
  grep -i foo | tr '[:lower:]' '[:upper:]'
  # as an example
}

if [ "$#" -gt 0 ]; then
  # feed arguments as separate lines through a pipe:
  printf '%s\n' "$@" | main
else
  # stdin just passed along
  main
fi

所有这些都可以通过以下方式调用:

cmd | the-script

或者:

the-script "$(cmd)"

(不要忘记引号,没有引号, 的输出cmd将受到 split+glob 的影响!)

也可以看看:

答案4

我的理解是,您必须单独考虑 1)设备,2)一般数据流接口,3)特别是线路读写器接口,4)语义。

stdout是您的流程私有的技术端点,具有相同的生命周期,用于写入字节流,而不是读取。您可以复制对它的引用,以便用另一个名称引用它,它的功能始终是输出。它是随进程一起创建和删除的。stdin是您读取字节流的等效项。它的能力始终是输入。根据底层设备的不同,它们可能具有这两种功能,但标准和可靠的模型对此并不可知。

A字节流是一个通用接口,在其写端接受两个信号:putclose并在其读端发出两个相应的信号:byte, EOD。当写端(例如stdout)接收到时put,读端必须得到一个byte。当写端accept时close,读端get EODEOD( ) 在描述符关闭时或进程终止时close显式发送。stdoutexec >&-

A行读取器或写入器是一种使用字节流的特殊方式,带有附加信号newline,但它不是stdinNor固有的stdout,它只是数据转换为和从文本行因为它方便、易操作且坚固。这是一个线路协议或纪律、历史标准、可迭代且合理不可知论者。信号newline是约定;无论它是什么,它都必须有一个值。除非依赖另一种状态,否则别无选择,但设备只有两种状态:打开或关闭。换句话说,字节流是设备驱动的,而行读取器是数据驱动的。

鉴于此,假设您想在两个进程之间进行通信通过一个管道,扬声器可以发送给听者的最小信号是byteEOD,但是这个不能通过迭代来完成正如您对文本行所做的那样,因为一旦关闭(EOD发送),文件描述符就是不可重复使用(即使它可能是可破解的)。但,只要你不需要迭代,您可以信赖EOD

然后,让我们现实一点:多态性正如你所说,如果你在这样做时丢失了可迭代的行模型,那么这不是字节流中的原始数据的问题。多态性是一个问题模型在一个简单的可迭代协议之上,比如行规则,在一个原始协议之上,比如字节流,可以在套接字、管道、文件、磁带、打印机或其他任何协议上实现。

然而,在流模型之上,只有两种迭代控制:要么你提前知道要读多少,要么你逐一读直到有信号。知道这一点后,您可以在原始流之上采用您喜欢的规则而不是文本行,这始终是一个问题解析器。解析器是一种读取流并以其自己的语言模型构建其他对象的机器。

而且,为了成为流顶部可接受的接口,它必须在上次迭代离开它的确切位置继续解析,按顺序向前。

现在我们知道了多态+迭代=>顺序解析器,我们可以回到标准并且非常满意。我们需要的是K埃普tS实施S愚蠢的。

相关内容