「時間」に応じてサブシェルを条件付きで渡す方法は?

「時間」に応じてサブシェルを条件付きで渡す方法は?

個々のステップを測定するために使用する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)'

ここで問題は何ですか?

答え1

行く能力がある時間必要なサブシェルtime キーワード、コマンドではありません。

キーワードはtime言語の一部であり、文字通り入力してコマンドの最初の単語として入力した場合にのみ認識されます(の場合、ksh93次のトークンはで始まりません-)。言うまでもなく、入力も"time"機能しません(そしてコマンド呼び出し$TIMEとして扱われます)。time

ここでは、別の解析ラウンドが実行される前に拡張されるエイリアスを使用できます(したがって、シェルに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 キーワードオプションは-p許可されません(除く)。ただし、書式設定に変数を使用できますbash。 (この部分も具体的であり、通常は他のシェルには必要ありません。)TIMEFORMATbashshoptbash

答え2

これはalias一つのアプローチですが、できるまた完了しました。コマンドを実行したくevalないのではなく、コマンドが欲しいものです。evaleval宣言

私は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 )また、シェル拡張の結果したがって、パラメータを解析可能な文字列に拡張するには"$*" eval "$@"(しかし、すべての引数が空白で連結できると確信していない限り、これを実行しないでください)。 args in間の区切り文字は"$*"の最初のバイトなので、$IFSその値を確信せずに進めるのは危険です。したがって、関数はを保存し、入れるのに十分な長さのewline$IFSに設定し、以前にその値があった場合はその値をリセットします。\nset ... "$*""$3"unset

以下は小さなデモです。

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

ご存知のように、私は両方のコマンドを値に入れました$TIME。どんな数でも可能です。なしでも同様です。ただし、引数と同様にエスケープされ、正しく引用されていることを確認してください_time()。実行時にすべてコマンド文字列にリンクされますが、各引数には独自の\newlineがあるため、比較的簡単に拡張できます。あるいは、必要に応じてそれらをすべてまとめて、行\n番号やセミコロンなどで区切ることもできます。単一の引数が呼び出されたときにスクリプトの別の行に安全に入力できるコマンドを示すことを確認してください。\(、例えば.が次に来る限り、大丈夫です\)。これは基本的に一般的なものです。

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

関連情報