_gem_dec() { shift $# ; . /dev/fd/3
} 3<<-FUNC
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
FUNC
for func in guard rspec rake ; do _gem_dec $func ; done
echo "_guard ; _rspec ; _rake are all functions now."
事前評価された唯一のジョブとして呼び出されるたび. source /dev/fd/3に_gem_dec()関数に渡される上記の意志here-document. _gem_dec'sは、1つのパラメーターを受け取り、それをbundle execターゲットとそのターゲットとなる関数の名前の両方として事前評価することです。
NOTE: . sourcing shell expansions results in twice-evaluated variables - just like eval. It can be risky.
上記の場合でも、リスクはないと思います。
上記のコードブロックが.bashrcファイルにコピーされると、シェル関数_guard(), _rspec()と_rake()ログイン時に宣言されるだけでなく、_gem_dec()関数はシェルプロンプト(またはその他)でいつでも実行できるため、新しいテンプレート関数で好きなときにいつでも宣言できます:
_gem_dec $new_templated_function_name
そして、私に見せてくれた@Andrewに感謝します。 for loop.
しかし、どうやって?
3上記のファイル記述子を使用しstdin, stdout, and stderr, or <&0 >&1 >&2て、癖を付けずに開いたままにします。ただし、ここで実装する他のいくつかのデフォルトの予防策の場合と同様に、結果の関数は非常に単純であるため、実際には必要ありません。しかし、それは良い習慣です。呼び出しshift $#は、これらの不要な予防策の1つです。
ファイルは次のように指定されている場合でも、<inputまたは>outputで[optional num]<fileまたは[optional num]>fileリダイレクトカーネルを介してアクセスすることができるファイルディスクリプタ、にそれを読み込んcharacter deviceで特殊ファイル/dev/fd/[0-9]*。[optional num]指定子が省略されている場合、0<file入力および1>file出力が想定されます。このことを考慮:
l='line %d\n' ; printf "$l" 1 2 3 4 5 6 >/dev/fd/1
> line 1
> line 2
> line 3
> line 4
> line 5
> line 6
( printf "$l" 4 5 6 >/dev/fd/3 ; printf "$l" 1 2 3 ) >/tmp/sample 3>/tmp/sample2
( cat /tmp/sample2 ) </tmp/sample
> line 4
> line 5
> line 6
( cat /dev/fd/0 ) </tmp/sample
> line 1
> line 2
> line 3
( cat /dev/fd/3 ) </tmp/sample 3</tmp/sample2
> line 4
> line 5
> line 6
また、a here-documentはコードブロック内でファイルをインラインで記述するための手段に過ぎないため、次のようにします。
<<'HEREDOC'
[$CODE]
HEREDOC
私たちもそうするかもしれません:
echo '[$CODE]' >/dev/fd/0
非常に重要な違いが1つあります。そうしない場合の、その後のシェルは、シェルのためにそれを評価するように:"'\quote'"<<"'\LIMITER"'here-document$expansion
echo "[$CODE]" >/dev/fd/0
したがって、の_gem_dec()場合、3<<-FUNC here-documentは入力時にファイルとして評価されます。3<~/some.file ただし、FUNCリミッターを引用符なしで残すため、最初に評価されます$expansion.。これについて重要なことは、入力であることです。つまり、それは存在するだけです_gem_dec(),が_gem_dec()、シェルは入力として渡す$expansions前にそれを読み取って評価する必要があるため、関数の実行前にも評価されます。
行うことができますguard,例えば:
_gem_dec guard
したがって、最初にシェルは入力を処理する必要があります。
3<<-FUNC
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
FUNC
ファイル記述子3に入れて、シェル拡張のために評価します。この時点で実行した場合:
cat /dev/fd/3
または:
cat <&3
どちらも同等のコマンドなので、次のように表示されます*:
_guard() { [ ! -e 'Gemfile' ] && {
command guard "$@" ; return $?
} || bundle exec guard "$@"
}
...これまで、関数内のコードはまったく実行されませんでした。結局のところ<input、これは関数のです。その他の例については、ここで別の質問に対する私の回答を参照してください。
(*技術的にはこれは完全に真実ではありません。の-dash前にリードを使用しているためhere-doc limiter、上記はすべて左揃えになります。しかし、最初は読みやすくする-dashためにを使用したので<tab-insert>、<tab-inserts>前を削除しません。読むためにあなたにそれを提供しています...)
これについて最も良い部分は引用です- '"引用符は残り、\引用符だけが取り除かれていることに注意してください。おそらくこの理由から、シェル$expansionを2回評価する必要がある場合はhere-document、引用符がに比べてはるかに簡単であるため、シェルを推奨しevalます。
とにかく、上記のコードは3<~/heredoc.file、_gem_dec()関数が実行されてでその入力を受け入れるのを待つだけのように、フィードされたファイルとまったく同じです/dev/fd/3。
したがって_gem_dec()、最初に行うことは、すべての位置パラメーターを投げることです。次のステップは、2回評価されたシェル展開であり、含ま$expansionsれているものを現在の$1 $2 $3...パラメーターとして解釈したくないためです。だから私:
shift $#
shiftpositional parameters指定した数だけ破棄$1し、残っているものから始めます。私はと呼ばれるのであれば_gem_dec one two threeプロンプト_gem_dec's $1 $2 $3になり、位置パラメータone two threeと、全電流位置のカウント、または$#私はそれから呼び出された場合は3になりますshift 2,の値oneとtwoされるだろうshift編離れて、の値が$1に変わってしまうthreeと$#に拡大する1.だから、shift $#ちょうどそれらをすべて捨てます。これを行うことは厳密に予防策であり、しばらくこのようなことを行った後に私が開発した習慣にすぎません。ここでは、(subshell)わかりやすくするために少し広げています。
( set -- one two three ; echo "$1 $2 $3" ; echo $# )
> one two three
> 3
( set -- one two three ; shift 2 ; echo "$1 $2 $3" ; echo $# )
> three
> 1
( set -- one two three ; shift $# ; echo "$1 $2 $3" ; echo $# )
>
> 0
とにかく、次のステップは魔法が起こるところです。. ~/some.shシェルプロンプトが表示された場合、で宣言されているすべての関数と環境変数は、シェルプロンプトで呼び出す~/some.shことができます。同じことが、私たちを除いて、ここに真である特別な私たちのファイルディスクリプタのためのファイル、または私たちのところである- そして私達は私達の機能を宣言した-のインラインファイルがパス化されています。そして、それはそれがどのように機能するかです。. sourcecharacter device. /dev/fd/3here-document
_guard
これで、_guard関数が行うことになっていることは何でも行います。
補遺:
ポジショナルを保存するための素晴らしい方法:
f() { . /dev/fd/3
} 3<<-ARGS
args='${args:-"$@"}'
ARGS
編集:
私が最初にこの質問に答えたとき、私function()は、現在のシェルの$ENVアイアンメントで持続する他の関数を宣言できるシェルを宣言するという問題に焦点を当てました。それ以来、私はもともと提供されていた解決策3<<-FUNCの形をとっていることに気づきました。
3<<-FUNC
_${1}() {
if [ -e 'Gemfile' ]; then
bundle exec $1 "\$@"
else
command _${1} "\$@"
}
FUNC
私は、具体的に宣言型関数の名前を変更されたため、アスカーのための予想通りそうに働いていないだろう$1と_${1}どのように呼び出された場合、_gem_dec guard例えば、につながる_gem_decという名前の関数の宣言_guardだけとは対照的に、をguard。
注:このような行動は、私にとっては習慣の問題である-私は通常、シェルの機能が占める必要があることを前提に操作するだけで、自分のを_namespace上の彼らの侵入を避けるために、namespaceシェルのcommands適切なを。
アスカーの使用にevincedされるように、これは、かかわらず、普遍的習慣ではないcommand時に呼び出します$1。
さらに詳しく調べてみると、次のことがわかります。
要求者はguard, rspec, or rake、呼び出されたときに、ファイルが存在するまたは存在しないruby同じ名前の関数を新しくコンパイルするという名前のシェル関数を望んでいます。シェル関数は同じ名前ifの関数を実行する必要があります。Gemfile$PATH
if Gemfileruby
私はまた、変更されたため、これが以前働いていないだろう$1によって呼びかけcommand読むために:
command _${1}
これはruby、シェル関数が次のようにコンパイルした関数の実行にはなりませんでした。
bundle exec $1
同じ名前のシェル関数よりも実行可能ファイルを呼び出すことを好むため、(最終的に私がしたように)質問者がcommand間接的に指定するnamespaceためだけに使用しているように見えることを望みます。command$PATH
私の分析が正しい場合(私は質問者が確認することを願っています)、これは:
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
より良い呼び出すことを除いて、これらの条件を満たさなければならないguardプロンプトがしますだけで実行可能ファイルを実行しよう$PATHという名前のguard呼び出しに対し、_guardチェックされますプロンプトでGemfile's存在し、それに応じて、コンパイルや実行guard、実行中に$PATH。このようにしnamespaceて保護され、少なくとも私が認識しているように、依頼者の意図はまだ満たされています。
実際には、私たちのシェル機能前提_${1}()と実行して${PATH}/${1}いるだけで私たちのシェルはどちらかへの呼び出しを解釈する可能性が二つの方法$1や_${1}、その後の使用commandの全ての機能では現在、完全に冗長化されています。それでも、同じ過ちを2回犯したくないのでそのままにしておきます。とにかく続けて。
これが質問者に受け入れられず、彼/彼女が_完全に取り除きたい場合、現在の形式では、_underscore私が理解しているように、彼/彼女の要件を満たすために質問者が行う必要があるのは、出力を編集することだけです。
その変更の他に、元の構文ではなく、使用する関数&&や||シェルの短絡条件を編集しましたif/then。このようにして、commandステートメントはがにない場合にのみ評価されます。ただし、この変更では、ステートメントが存在しない場合にステートメントが実行されないようにするためにを追加する必要がありますが、関数は0以外を返します。Gemfile$PATHreturn $?bundleGemfileruby $1
最後に、このソリューションは移植可能なシェル構造のみを実装することに注意してください。言い換えれば、これはPOSIX互換性を主張するどのシェルでも同じ結果を生成するはずです。私は、すべてのPOSIX互換システムが処理しなければならないと主張するのは、当然のことながら、ナンセンスであろうがruby bundleディレクティブを、それ呼びかけ少なくともシェルの要請にかかわらず、呼び出し元のシェルがあるかどうかの同じように振る舞うべきshかdash。また、上記の意志の作業予想通り(途中-健全少なくとも推定shoptsとにかく)の両方にbash及びzsh。
for loop?、for loop一般的に宣言されている変数は消えます-同じ理由で同じ関数を期待します。