bash 如何制作一个可以从标准输入读取的bash函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18761209/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 00:11:04  来源:igfitidea点击:

How to make a bash function which can read from standard input?

bashpipestdin

提问by JBoy

I have some scripts that work with parameters, they work just fine but i would like them to be able to read from stdin, from a pipe for example, an example, suppose this is called read:

我有一些使用参数的脚本,它们工作得很好,但我希望它们能够从标准输入读取,例如从管道读取,例如,假设这称为读取:

#!/bin/bash
function read()
{
 echo $*
}

read $*

Now this works with read "foo" "bar", but I would like to use it as:

现在这适用于read "foo" "bar",但我想将其用作:

echo "foo" | read

How do I accomplish this?

我该如何实现?

采纳答案by fedorqui 'SO stop harming'

You can use <<<to get this behaviour. read <<< echo "text"should make it.

您可以使用<<<来获取此行为。read <<< echo "text"应该做到。

Test with readly(I prefer not using reserved words):

测试readly(我不喜欢使用保留字):

function readly()
{
 echo $*
 echo "this was a test"
}

$ readly <<< echo "hello"
hello
this was a test

With pipes, based on this answer to "Bash script, read values from stdin pipe":

使用管道,基于对“Bash 脚本,从标准输入管道读取值”的回答

$ echo "hello bye" | { read a; echo $a;  echo "this was a test"; }
hello bye
this was a test

回答by chepner

It's a little tricky to write a function which can read standard input, but works properly when no standard input is given. If you simply try to read from standard input, it will block until it receives any, much like if you simply type catat the prompt.

编写一个可以读取标准输入但在没有给出标准输入时正常工作的函数有点棘手。如果您只是尝试从标准输入中读取,它将阻塞直到收到任何,就像您只是cat在提示符下键入一样。

In bash 4, you can work around this by using the -toption to readwith an argument of 0. It succeeds if there is any input available, but does not consume any of it; otherwise, it fails.

在 bash 4 中,您可以通过使用参数为 0的-t选项来解决这个问题。read如果有任何可用输入,它会成功,但不消耗任何输入;否则,它将失败。

Here's a simple function that works like catif it has anything from standard input, and echootherwise.

这是一个简单的函数,它的工作原理就像cat它有来自标准输入的任何东西一样,echo否则。

catecho () {
    if read -t 0; then
        cat
    else
        echo "$*"
    fi
}

$ catecho command line arguments
command line arguments
$ echo "foo bar" | catecho
foo bar

This makes standard input take precedence over command-line arguments, i.e., echo foo | catecho barwould output foo. To make arguments take precedence over standard input (echo foo | catecho baroutputs bar), you can use the simpler function

这使得标准输入优先于命令行参数,即echo foo | catecho bar会输出foo。要使参数优先于标准输入(echo foo | catecho bar输出bar),您可以使用更简单的函数

catecho () {
    if [ $# -eq 0 ]; then
        cat
    else
        echo "$*"
    fi
}

(which also has the advantage of working with anyPOSIX-compatible shell, not just certain versions of bash).

(这也具有使用任何POSIX 兼容 shell的优点,而不仅仅是某些版本的bash)。

回答by Andy

To combine a number of other answers into what worked for me (this contrived example turns lowercase input to uppercase):

将许多其他答案组合成对我有用的答案(这个人为的例子将小写输入转换为大写):

  uppercase() {
    local COMMAND='tr [:lower:] [:upper:]'
    if [ -t 0 ]; then
      if [ $# -gt 0 ]; then
        echo "$*" | ${COMMAND}
      fi
    else
      cat - | ${COMMAND} 
    fi
  }

Some examples (the first has no input, and therefore no output):

一些示例(第一个没有输入,因此没有输出):

:; uppercase
:; uppercase test
TEST
:; echo test | uppercase 
TEST
:; uppercase <<< test
TEST
:; uppercase < <(echo test)
TEST


Step by step:

一步步:

  • test if file descriptor 0 (/dev/stdin) was opened by a terminal

    if [ -t 0 ]; then
    
  • tests for CLI invocation arguments

    if [ $# -gt 0 ]; then
    
  • echo all CLI arguments to command

    echo "$*" | ${COMMAND}
    
  • else if stdinis piped (i.e. not terminal input), output stdinto command (cat -and catare shorthand for cat /dev/stdin)

    else
      cat - | ${COMMAND}
    
  • 测试文件描述符 0 ( /dev/stdin) 是否被终端打开

    if [ -t 0 ]; then
    
  • CLI 调用参数的测试

    if [ $# -gt 0 ]; then
    
  • 将所有 CLI 参数回显到命令

    echo "$*" | ${COMMAND}
    
  • else ifstdin是通过管道传输的(即不是终端输入),输出stdin到命令(cat -并且cat是 的简写cat /dev/stdin

    else
      cat - | ${COMMAND}
    

回答by kenorb

Here is example implementation of sprintffunction in bash which uses printfand standard input:

这是sprintf使用printf标准输入的bash 函数的示例实现:

sprintf() { local stdin; read -d '' -u 0 stdin; printf "$@" "$stdin"; }

Example usage:

用法示例:

$ echo bar | sprintf "foo %s"
foo bar

This would give you an idea how function can read from standard input.

这会让您了解函数如何从标准输入中读取。

回答by zwbetz

Late to the party here. Building off of @andy's answer, here's how I define my to_uppercasefunction.

这里的派对迟到了。基于@andy答案,这是我定义to_uppercase函数的方式。

  • if stdin is not empty, use stdin
  • if stdin is empty, use args
  • if args are empty, do nothing
  • 如果标准输入不为空,使用标准输入
  • 如果 stdin 为空,则使用 args
  • 如果 args 为空,则什么都不做
to_uppercase() {
  local input="$([[ -p /dev/stdin ]] && cat - || echo "$@")"
  [[ -n "$input" ]] && echo "$input" | tr '[:lower:]' '[:upper:]' 
}

Usages:

用法:

$ to_uppercase 
$ to_uppercase abc
ABC
$ echo abc | to_uppercase 
ABC
$ to_uppercase <<< echo abc 
ABC

Bash version info:

Bash 版本信息:

$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)

回答by DocSalvager

I've discovered that this can be done in one line using testand awk...

我发现这可以在一行中使用testawk...

    test -p /dev/stdin  && awk '{print}' /dev/stdin

The test -ptests for input on a pipe, which accepts input via stdin. Only if input is present do we want to run the awksince otherwise it will hang indefinitely waiting for input which will never come.

test -p上一个管,该管通过stdin接受输入用于输入测试。只有当输入存在时,我们才想要运行,awk否则它将无限期地挂起等待永远不会到来的输入。

I've put this into a function to make it easy to use...

我已将其放入一个函数中以使其易于使用...

inputStdin () {
  test -p /dev/stdin  && awk '{print}' /dev/stdin  && return 0
  ### accepts input if any but does not hang waiting for input
  #
  return 1
}

Usage...

用法...

_stdin="$(inputStdin)"

Another function uses awk without the test to wait for commandline input...

另一个函数使用没有测试的 awk 来等待命令行输入......

inputCli () {
  local _input=""
  local _prompt=""
  #
  [[ "$_prompt" ]]  && { printf "%s" "$_prompt" > /dev/tty; }
  ### no prompt at all if none supplied
  #
  _input="$(awk 'BEGIN {getline INPUT < "/dev/tty"; print INPUT}')"
  ### accept input (used in place of 'read')
  ###   put in a BEGIN section so will only accept 1 line and exit on ENTER
  ###   WAITS INDEFINITELY FOR INPUT
  #
  [[ "$_input" ]]  && { printf "%s" "$_input"; return 0; }
  #
  return 1
}

Usage...

用法...

_userinput="$(inputCli "Prompt string: ")"

Note that the > /dev/ttyon the first printfseems to be necessary to get the prompt to print when the function is called in a Command Substituion $(...).

请注意,当在 Command Substituion 中调用该函数时> /dev/tty,第一个printf似乎是获取提示打印所必需的$(...)

This use of awkallows the elimination of the quirky readcommand for collecting input from keyboard or stdin.

这种使用awk允许消除read从键盘或标准输入收集输入的古怪命令。