引数とともにコマンドを渡し、関数にリダイレクト

引数とともにコマンドを渡し、関数にリダイレクト

Bashスクリプトはパラメータとともにコマンドを渡します。コマンドにリダイレクトが含まれていない場合は正常に動作します。この場合、リダイレクト文字は通常の文字として扱われます。

$ cat foo
#!/bin/bash

f() {
  echo "command: $@"
  $@
}

f echo a-one a-two
f 'echo b-one b-two'
f 'echo c-one c-two > c.tmp'
# I don't want to do f echo d-one d-two > d.tmp because I want to redirect the
# output of the passed command, not the output of the f() function.

$ ./foo
command: echo a-one a-two
a-one a-two
command: echo b-one b-two
b-one b-two
command: echo c-one c-two > c.tmp
c-one c-two > c.tmp

ご覧のとおり、「c-one c-two」をc.tmpファイルに印刷しようとすると、「c-one c-two> c.tmp」が印刷されます。これを行う方法はありますか?

ベストアンサー1

f() {
  echo "command: $@"
  $@
}

一般的に言えば、あなたは使用すべき"$@"、ここもあります。通常 理由

しかし、あなたは正しいです。このようなコマンドを実行すると、シェル演算子は処理されず、リダイレクトまたは&&eval

特に、任意のファイル名を渡すことは再び困難になります。たとえば、これは最終的に次のls2つのファイルで実行されますfoobar

run_with_eval() {
    eval "$@"
}
file='foo bar'
run_with_eval ls -l "$file" 

あなたはする必要があります参照するファイル名の並べ替えこれはシェルにやや厄介でエラーが発生しやすいです。

しかし、もしあなたがただリダイレクトまたはより良い方法で出力専用のリダイレクトを実行するには、関数に>それを手動で処理させることができます。

#!/bin/bash
run_with_redir() {
    local redir=
    if [[ $1 = '>' ]]; then
        redir=$2
        shift 2
    fi
    ## whatever else you do
    if [[ $redir ]]; then
        "$@" > "$redir"
    else
        "$@"
    fi
}

run_with_redir echo "normal command, no redirection"
run_with_redir ">" output.txt echo "this gets redirected to output.txt"

つまり、>ファイル名を最初の2つの引数として関数を呼び出し、残りの引数をコマンドとして実行し、出力をリダイレクトします。そうでない場合は、>通常どおりコマンドを実行します。">"関数を呼び出すときは、引用符を入れる必要があります。それ以外の場合は、関数全体の出力リダイレクトが再び発生します。たとえば、それをサポートするために拡張することは、冗長であっても>>マイナーなことです。

これはより厳しい場合でも機能します。

run_with_redir ">" "output file with space.txt" ls -l "other file with space"

おすすめ記事