web-dev-qa-db-ja.com

Bash:スクリプトで標準入力を動的にリダイレクトする

Stdinをファイルにリダイレクトするかどうかを決定するためにこれを試みました:

[ ...some condition here... ] && input=$fileName || input="&0"
./myScript < $input

しかし、変数$ inputが「&0」の場合、bashはそれをファイル名として解釈するため、機能しません。

しかし、私はただ行うことができました:

if [ ...condition... ];then
    ./myScript <$fileName
else
    ./myScript

問題は、。/ myScriptが実際には長いコマンドラインであり、複製したくない、またはそのために関数を作成したくないことです。これもそれほど長くないためです(価値がありません)。

次に、これを行うことが私に起こりました:

[ ...condition... ] && input=$fileName || input=  #empty
cat $input | ./myScript

しかし、そのためには、もう1つのコマンドとパイプ(つまり、サブシェル)を実行する必要があります。
もっと簡単で効率的な方法はありますか?

29
GetFree

まず、stdinは1(stdout)ではなく、ファイル記述子0(ゼロ)です。

ファイル記述子を複製するか、次のように条件付きでファイル名を使用できます。

[[ some_condition ]] && exec 3<"$filename" || exec 3<&0

some_long_command_line <&3

表示されているコマンドは、いずれかの条件がfalseの場合、2番目のexecを実行することに注意してくださいor最初のexecは失敗します。失敗の可能性を望まない場合は、if/elseを使用する必要があります。

if [[ some_condition ]]
then
    exec 3<"$filename"
else
    exec 3<&0
fi

ただし、最初のリダイレクトが失敗した場合(条件がtrueになった後)、ファイル記述子3からのその後のリダイレクトは失敗します。

24

標準入力は、特殊なデバイスファイル/dev/stdinで表すこともできるため、ファイル名として使用すると機能します。

file="/dev/stdin"
./myscript < "$file"
(
    if [ ...some condition here... ]; then
        exec <$fileName
    fi
    exec ./myscript
)

サブシェルで、条件付きでstdinをリダイレクトし、スクリプトを実行します。

7
ephemient

いかがですか

_function runfrom {
    local input="$1"
    shift
    case "$input" in
        -) "[email protected]" ;;
        *) "[email protected]" < "$input" ;;
    esac
}
_

標準入力を表すためにマイナス記号を使用しましたが、これは多くのUnixプログラムにとって伝統的なものだからです。

今あなたは書く

_[ ... condition ... ] && input="$fileName" || input="-"
runfrom "$input" my-complicated-command with many arguments
_

コマンドを引数としてとるこれらの関数/コマンド(xargs(1)など)は非常に便利で、うまく構成できます。

2
Norman Ramsey

注意する場合は、「eval」と最初のアイデアを使用できます。

[ ...some condition here... ] && input=$fileName || input="&1"
eval ./myScript < $input

ただし、「myScript」は実際には複雑なコマンド呼び出しであると言います。スペースを含む可能性のある引数が含まれる場合は、 'eval'の使用を決定する前に十分に注意する必要があります。

率直に言って、 'cat'コマンドのコストについて心配することは、おそらく問題の価値はありません。ボトルネックになることはまずありません。

通常のUnixフィルターのように機能するようにmyScriptを設計することはさらに優れています-機能するファイルが1つ以上指定されていない限り(たとえば、catまたはgrepなど)、標準入力から読み取ります。その設計は長く健全な経験に基づいているため、このような問題に対処する必要がないようにエミュレートする価値があります。

2

eval を使用:

#! /bin/bash

[ $# -gt 0 ] && input="'"$1"'" || input="&1"

eval "./myScript <$input"

このmyScriptの簡単な代用

#! /usr/bin/Perl -lp
$_ = reverse

次の出力が生成されます。

 $ ./myDemux myScript 
 pl- lrep/nib/rsu /!#
 esrever = _ $ 
 
 $ ./myDemux 
 foo 
 oof 
 bar 
 rab 
 baz 
 zab 

入力のスペースも処理することに注意してください。

 $ ./myDemux foo\bar 
 eman eht ni ecaps a htiw Elif 

入力をmyScriptにパイプダウンするには、 プロセス置換 を使用します。

 $ ./myDemux <(md5sum /etc/issue)
eussi/cte/ 01672098e5a1807213d5ba16e00a7ad0 

次のように出力を直接パイプしようとすると、

 $ md5sum/etc/issue | ./myDemux

ephemient's answer にはこの欠点はありませんが、ターミナルからの入力を待ってハングします。

わずかな変更により、望ましい動作が生成されます。

#! /bin/bash

[ $# -gt 0 ] && input="'"$1"'" || input=/dev/stdin
eval "./myScript <$input"
1
Greg Bacon

人々はあなたに非常に長いスクリプトを見せます、しかしあなたはbashトラップを受け取ります:)あなたはすべてをbashで引用しなければなりません。たとえば、&0という名前のリストファイルが必要です。

filename = '&0' #right ls $ filename #wrong!これは$ filenameを置き換え、&0 ls "$ filename" #rightを解釈します

別の、スペースを含むファイル。

filename = 'スペースのあるファイル' ls $ filename #wrong、最初と最後のスペースをbashでカットし、withとスペースの間にある複数のスペースを減らしますls "$ filename" righ

スクリプトでも同じです。どうか変更してください:

./myScript < $input

./myScript < "$input"

そのすべて。 bashにはより多くのトラップがあります。同じ理由で「$ file」も引用することをお勧めします。解釈できないスペースやその他の文字は常に問題を引き起こします。

/ dev/stdinはどうですか?これは、stdinをリダイレクトして何かを実際のstdinに出力したい場合にのみ使用できます。

したがって、スクリプトは次のように表示されます。

[ ...some condition here... ] && input="$fileName" || input="&0"
./myScript < "$input"
0
Znik