私はパラメータで動作するいくつかのスクリプトを持っていますが、それらはうまく動作しますが、たとえばパイプなどの標準入力から読み取ることができるようにしたいと思います。
#!/bin/bash
function read()
{
echo $*
}
read $*
現在、これはread "foo" "bar"
、しかし私はそれを次のように使いたい:
echo "foo" | read
どうすればこれを達成できますか?
<<<
を使用して、この動作を取得できます。 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
標準入力を読み取ることができる関数を作成するのは少し難しいですが、標準入力が指定されていない場合は適切に動作します。単に標準入力から読み取ろうとすると、プロンプトで単に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 bar
はfoo
を出力します。引数を標準入力(echo foo | catecho bar
出力bar
)より優先させるには、より単純な関数を使用できます
catecho () {
if [ $# -eq 0 ]; then
cat
else
echo "$*"
fi
}
(これは、特定のバージョンのbash
だけでなく、any POSIX互換シェルで動作するという利点もあります)。
他の多くの回答を組み合わせて、私にとってうまくいったものにするには(この不自然な例では、小文字の入力を大文字に変えます):
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 -
およびcat
はcat /dev/stdin
の省略形です)
else
cat - | ${COMMAND}
sprintf
と標準入力を使用するbashのprintf
関数の実装例は次のとおりです。
sprintf() { local stdin; read -d '' -u 0 stdin; printf "$@" "$stdin"; }
使用例:
$ echo bar | sprintf "foo %s"
foo bar
これにより、関数が標準入力からどのように読み取ることができるかがわかります。
これは、test
とawk
を使用して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
コマンドを排除できます。
ここでパーティーに遅れて。 @andy
's answer 、ここに私のto_uppercase
関数。
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)