web-dev-qa-db-ja.com

標準入力から読み取ることができるbash関数を作成する方法は?

私はパラメータで動作するいくつかのスクリプトを持っていますが、それらはうまく動作しますが、たとえばパイプなどの標準入力から読み取ることができるようにしたいと思います。

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

read $*

現在、これはread "foo" "bar"、しかし私はそれを次のように使いたい:

echo "foo" | read

どうすればこれを達成できますか?

41
JBoy

<<<を使用して、この動作を取得できます。 read <<< echo "text"で作成する必要があります。

readlyを使用してテストします(予約語を使用しないことをお勧めします)。

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

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

「bashスクリプト、stdinパイプから値を読み取る」に対する回答 に基づくパイプを使用:

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

標準入力を読み取ることができる関数を作成するのは少し難しいですが、標準入力が指定されていない場合は適切に動作します。単に標準入力から読み取ろうとすると、プロンプトで単にcatと入力するのと同じように、何かを受け取るまでブロックします。

Bash 4では、read-tオプションを引数0で使用することでこれを回避できます。利用可能な入力があれば成功しますが、そのいずれも消費しません。そうでない場合、失敗します。

以下は、標準入力から何かを持っている場合は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

これにより、標準入力がコマンドライン引数より優先されます。つまり、echo foo | catecho barfooを出力します。引数を標準入力(echo foo | catecho bar出力bar)より優先させるには、より単純な関数を使用できます

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

(これは、特定のバージョンのbashだけでなく、any POSIX互換シェルで動作するという利点もあります)。

37
chepner

他の多くの回答を組み合わせて、私にとってうまくいったものにするには(この不自然な例では、小文字の入力を大文字に変えます):

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

いくつかの例(最初の例には入力がなく、したがって出力もありません):

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

ステップバイステップ:

  • ファイル記述子0(/dev/stdin)が端末によって開かれたかどうかをテストします

    if [ -t 0 ]; then
    
  • cLI呼び出し引数のテスト

    if [ $# -gt 0 ]; then
    
  • すべてのCLI引数をコマンドにエコーします

    echo "$*" | ${COMMAND}
    
  • それ以外の場合、stdinがパイプされている(つまり、端末入力ではない)場合、コマンドにstdinを出力します(cat -およびcatcat /dev/stdinの省略形です)

    else
      cat - | ${COMMAND}
    
14
Andy

sprintfと標準入力を使用するbashのprintf関数の実装例は次のとおりです。

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

使用例:

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

これにより、関数が標準入力からどのように読み取ることができるかがわかります。

5
kenorb

これは、testawkを使用して1行で実行できることを発見しました...

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

_test -p_は、stdinを介して入力を受け入れるパイプの入力をテストします。入力が存在する場合にのみ、awkを実行します。それ以外の場合、入力されない入力を無期限に待機してハングします。

これを関数に入れて使いやすくしました...

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

使用法...

__stdin="$(inputStdin)"
_

別の関数は、テストなしでawkを使用してコマンドライン入力を待機します...

_inputCli () {
  local _input=""
  local _Prompt="$1"
  #
  [[ "$_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
}
_

使用法...

__userinput="$(inputCli "Prompt string: ")"
_

最初のprintfの_> /dev/tty_は、コマンド置換$(...)で関数が呼び出されたときにプロンプ​​トを表示するために必要であることに注意してください。

このawkの使用により、キーボードまたはstdinから入力を収集するための風変わりなreadコマンドを排除できます。

0
DocSalvager

ここでパーティーに遅れて。 @andy 's answer 、ここに私のto_uppercase 関数。

  • stdinが空でない場合は、stdinを使用します
  • stdinが空の場合は、argsを使用します
  • 引数が空の場合、何もしません
to_uppercase() {
  local input="$([[ -p /dev/stdin ]] && cat - || echo "$@")"
  [[ -n "$input" ]] && echo "$input" | tr '[:lower:]' '[:upper:]' 
}

使用法:

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

Bashバージョン情報:

$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-Apple-darwin17)
0
zwbetz