条件付きで「時間」を通じてサブシェルを渡すにはどうすればよいですか?


9

Vagrantボックスのセットアップスクリプトがあります。これは、でシングルステップを測定するために使用していましたtime。次に、時間測定を条件付きで有効または無効にしたいと思います。

たとえば、以前の行は次のようになります。

time (apt-get update > /tmp/last.log 2>&1)

今、私は単にこのようなことをすることができると思いました:

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && TIME="time --format=%e" || TIME=""

$TIME (apt-get update > /tmp/last.log 2>&1)

しかし、これはうまくいきません:

syntax error near unexpected token `apt-get'
`$TIME (apt-get update > /tmp/last.log 2>&1)'

ここの問題は何ですか?


3
フラックスコンデンサーが機能するためには、目標速度に到達する必要があります;)
goldilocks 2014

2
@goldilocks 磁石ですか?;)
CVn 2014

回答:


13

サブシェルの時間を計測できるようにするには、コマンドではなく、time キーワードが必要です。

time文字通り入ったとき、キーワード、言語の部分は、唯一のような認識され、コマンドの最初の単語として(との場合にはksh93、次のトークンは始まりません-)。入力したとしても機能し"time"ません$TIMEtime代わりにコマンドの呼び出しと見なされます)。

別のラウンドの解析が実行される前に展開されるエイリアスをここで使用できます(そのため、シェルにそのtimeキーワードを認識させます)。

shopt -s expand_aliases
alias time_or_not=
TIMEFORMAT=%E

MEASURE_TIME=true
[[ $MEASURE_TIME = true ]] && alias time_or_not=time

time_or_not (apt-get update > /tmp/last.log 2>&1)

time キーワードは、(以外の選択肢を取らない-pbash)が、フォーマットがで設定できるTIMEFORMAT変数bash。(このshopt部分もbash-Specificであり、他のシェルは一般にそれを必要としません)。


「サブシェルの時間を計測できるようにするには、timeキーワードが必要です」と言いました。この動作はどこに記載されているのでしょうか?
cuonglm 2014

@cuonglmは、参照info -f bash --index-search=time
ステファンChazelas

3

一方でaliasそれを行うための一つの方法である、これが可能で行うことevalとしても-それはあなたがにそんなにないしたいですだけということだevalあなたがしたいようにコマンド実行をeval指示宣言

私はaliasesが好きです-私はいつも使いますが、関数の方が好きです。特に、パラメータを処理する能力と、aliasesに必要なようにコマンド位置で必ずしも拡張する必要がないことです。

だから、あなたもこれを試してみたいと思いました:

_time() if   set -- "${IFS+IFS=\$2;}" "$IFS" "$@" && IFS='
';      then set -- "$1" "$2" "$*"; unset IFS
             eval "$1 $TIME ${3#"$1"?"$2"?}"
        fi

$IFSビットは主についてです$*。ことが重要だ( subshell bit )である、シェル拡張の結果とその解析可能な文字列Iの使用に引数を拡大するためには"$*" (ないeval "$@"、ところで、あなたは引数のいる特定のすべてがスペースに参加することができない場合)。引数間の分割区切り文字"$*"はの最初のバイトである$IFSため、その値を確認せずに続行すると危険な場合があります。したがって、関数はを保存し$IFS、それをに\n入る十分な長さのewlineに設定set ... "$*""$3"unset以前に値があった場合はその値をリセットします。

ここに小さなデモがあります:

TIME='set -x; time'
_time \( 'echo "$(echo any number of subshells)"' \
         'command -V time'                        \
         'hash time'                              \
      \) 'set +x'

私はそこに2つのコマンドを入れました$TIME-どんな数値でも結構です-なしでも-エスケープされ、適切に引用されていることを確認してください-同じことがへの引数にも当てはまります_time()。それらは実行時にすべて1つのコマンド文字列に連結されますが、各引数は独自の\newlineを取得するため、比較的簡単に展開できます。または、必要に応じてそれらを1つに\nまとめて、それらをewlineまたはセミコロンまたはwhat-have-you で区切ることもできます。単一の引数が、それを呼び出すときにスクリプト内の独自の行に置くのが快適だと思うコマンドを表すことを確認してください。\(たとえば、最終的にが付いている限り、問題ありません\)。基本的には通常のものです。

ときeval上記のスニペットはそれを与えますそれは次のようになります。

+ eval 'IFS=$2;set -x; time (
echo "$(echo any number of subshells)"
command -V time
hash time
)
set +x'

そして、その結果は次のようになります...

出力

+++ echo any number of subshells
++ echo 'any number of subshells'
any number of subshells
++ command -V time
time is a shell keyword
++ hash time
bash: hash: time: not found

real    0m0.003s
user    0m0.000s
sys     0m0.000s
++ set +x

hashエラーは、私が持っていないことを示して/usr/bin/timeインストール(私はないので)command、私たちが実行している時間を知ってみましょう。末尾がset +x実行され、別のコマンドです後に time (可能) -時に入力コマンドに注意することが重要であるeval何かをする。


それを作らない理由は_time() { eval "$TIME $@"; }何ですか?機能変数の異なるスコープを導入する欠点(サブシェルのためではない問題)を有し用い
ステファンChazelas

@StéphaneChazelas- "$@"展開内の引用のため。私は単一の引数が好きですeval-そうでなければ怖いです。うーん....しかし、別のスコープについてどういう意味ですか?それは単なるfunction機能だと思っていました...
mikeserv '18 / 12/18

eval実行前にその引数を結合します。これは非常に複雑な方法で実行しようとしていることです。私はそれが意味_time 'local var=1; blah'それはなるだろうvarとローカル_time、またはそれが_time 'echo "$#"'印刷し$#ているの_timeではない、発信者のことを、機能を。
ステファンChazelas

@StéphaneChazelas-2つのタブ補完がここにあります。右- evalスペースでその引数を連結します-これは、あなたが言うように、まさにここで私が行うことです-それは最初の意図ではありませんでした。私はそれを修正します。反対にevaling は、引数が間違った場所で結合されたときに、多くの慣らしの後に私が見つけた習慣です。いずれにせよ、argsは最終的には関数内で実行されますが、呼び出し時にそれらを展開するのは十分簡単だと思います。それはとにかく私が何をするかです。"$*""$@"command not found"$#"
mikeserv 2014

しかし牛車の素敵なねじれた部分のための1 ...
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.