エイリアスコマンドを持つ関数はevalで使用できますが、シェル拡張には使用できません。

エイリアスコマンドを持つ関数はevalで使用できますが、シェル拡張には使用できません。

以下は私が実行したい機能です。問題は、main_cmd他のコマンドのエイリアスに関連しているようです。私はこれがサブシェルを作成するときの問題にすぎないと思いますが、単純な配列拡張ではなぜこれが起こりますか? IIUC サブシェルは作成されません。

my_function() {
    ... code to set opt1 based on $1 ...
    cmd=(
        main_cmd hard_coded_arg_1 hard_coded_arg_2
        -a "$opt1"
        -b hard_coded_opt
        "'$2'"
    )

    # Doesn't work.
    "$cmd[@]"
    # Works
    eval "$cmd[@]"
}

ただ使用したいのですがeval半パターンのようですね(なぜ表現にだけ重要なのか理解してはいませんが)処刑されるまっすぐ、しかしそれは別のトピックです)。

編集:これを明確にするためにzsh

答え1

エイリアスは特別です。構文解析が完全に完了する前にコマンドライン処理の最初に処理され、別の関数を呼び出すのではなく、直接テキスト置換のように機能します。

エイリアスを使用すると、次のことができます。

% alias maybe=if
% maybe true; then echo yes; fi
yes

または

% alias foo='echo hi; '
% foo echo bar
hi
bar

でも

% alias xyz='echo "foo'
% xyz bar"
foo bar

2番目のケースでは、foo echo bar翻訳され、echo hi; echo bar一般的な方法で解析されます。関数の場合、コマンド名と2つのパラメータで解析された後、シェルは後でそれがfoo関数か、組み込みコマンドか、外部コマンドかを決定する必要があります。最後の場合、xyz bar"エイリアスが存在しない場合は、コマンドに閉じられていない引用符が表示されます。

上記の例のどれも関数と連携しません。(まあ、とにかく彼らは一種の「働き」をします。誰かは彼らがもっと似ていると言うかもしれません)。残りシェル構文。 )

ただし、これは"${cmd[@]}"解析と拡張後にエイリアス処理がずっと前に消えたことを意味します。実際には、次のようにすることができます。

% alias runcmd='"${cmd[@]}"'
% cmd=(printf "'%s'\n" "foo bar" abc)
% runcmd
'foo bar'
'abc'

これは、アレイ拡張が完了する前にエイリアスが処理されたことを示します。

を使用すると、エイリアス拡張を含む別のコマンドライン処理を強制するevalという点で「動作」します。evalただし、実際には不要なコマンドパラメータの引用符の処理も含まれます。バラよりステファンの詳細な回答この点について。

main_cmdエイリアスの代わりに関数を使用すると、あまり混乱しない可能性があります。

答え2

あなたが望むもの:

cmd=(
  main_cmd hard_coded_arg_1 hard_coded_arg_2
  -a "$opt1"
  -b hard_coded_opt
  "$2"
)
"${cmd[@]}"

次の要素を含む配列を定義します。

  1. main_cmd
  2. hard_coded_arg_1
  3. hard_coded_arg_2
  4. -a
  5. value-of-opt1-variable
  6. -b
  7. hard_coded_opt
  8. value-of-second-positional-parameter

その後、これは各引数に個別に拡張"${cmd[@]}"(またはzshから)されるため、配列内のすべての要素を引数として使用して、配列の"$cmd[@]"最初の要素()で検索するコマンドを実行します。$cmd[1]

zshでは、justを使用することも$cmdここで機能しますが、空の要素を削除するため、任意のコマンドと一緒に使用することはできません。

または(配列を使用することはほとんど意味がありませんが):

cmd=(
  'main_cmd hard_coded_arg_1 hard_coded_arg_2'
  '-a "$opt1"'
  '-b hard_coded_opt'
  '"$2"'
)
eval " ${cmd[@]}"

アレイには以下が含まれます。

  1. main_cmd hard_coded_arg_1 hard_coded_arg_2
  2. -a "$opt1"
  3. -b hard_coded_opt
  4. "$2"

私たちはevalそれを別の引数として渡します(オプションになることができるaが含まれている場合は、最初の引数の前にスペースを追加します-eval。途中で空白と連結すると、次のようになります。main_cmd hard_coded_arg_1 hard_coded_arg_2 -a "$opt1" -b hard_coded_opt "$2"その後、シェルコードとして解釈されます。 )eval

行為:

cmd=(
    main_cmd hard_coded_arg_1 hard_coded_arg_2
    -a "$opt1"
    -b hard_coded_opt
    "'$2'"
)

eval "$cmd[@]"

あなたが得る配列の要素は次のようなので間違っています。

  1. main_cmd
  2. hard_coded_arg_1
  3. hard_coded_arg_2
  4. -a
  5. value-of-opt1-variable
  6. -b
  7. hard_coded_opt
  8. 'value-of-second-positional-parameter'

だからあなたは説明を終えるでしょうeval

main_cmd hard_coded_arg_1 hard_coded_arg_2 -a value-of-opt1-variable -b hard_coded_opt 'value-of-second-positional-parameter'

value-of-opt1-variableそれが存在するか$(reboot)含まれている場合$2は、'; rm -rf ~ #悲惨な結果を想像することができます。

zsh では、eval " $cmd"配列要素間の最初の文字を使用して配列要素を連結することが短くなります。スペースで始まるデフォルト値には問題はありませんが、変更されたコンテキストで使用すると中断されます(明示的な分割演算子と接続演算子を持つzshでは一般的ではないため、変更はほとんど必要ありません)。eval " ${cmd[*]}"$IFS$IFS$IFS$IFS

ここで目的がコマンドを複数行に分けて読みやすくすることであれば、ほとんどすべてのシェルで動作する従来のアプローチは次のようなものです。行連続行末にバックスラッシュを挿入すると、次のようになります。

my_function() {
  main_cmd hard_coded_arg_1 hard_coded_arg_2 \
    -a "$opt1" \
    -b hard_coded_opt \
    "$2"
}

連続した行は通常、別々の行ではなく、連続した行であることをより明確にするためにインデントされます。一部の人々は、次のように書くことを好む。

my_function() {
  main_cmd hard_coded_arg_1 hard_coded_arg_2 \
    -a "$opt1"                               \
    -b hard_coded_opt                        \
    "$2"
}

\これにより、aが見つからない場合をより簡単に検出できます。

とにかくここではエイリアスを使いたくないでしょう。エイリアスは非常に最初に拡張されるという点で、Cプリプロセッサマクロと少し似ています(Cの前処理ステップでシェルから読み取ってコードを表示し、その構文を部分的に解析した後)。ただし、Cとは異なり、次の場合にのみ拡張された後にのみ可能です。コマンド位置²そして議論を受け入れないでください。

array=( alias_name ... )これは、コマンドの場所に見つからないために拡張されない理由を説明します。alias_name

一部の条件では、コードで再評価される一部のテキストで置き換えられるトークンではなく、実際に実際のコマンドを定義するため、main_cmd() the command "$@"置換を使用するのに問題はありません。alias main_cmd='the command'main_cmd


1 エイリアス拡張の後、結果の構文が再解析され、コマンドの意味が完全に変わったり、別のエイリアス拡張ラウンドが続行されたりする可能性があります。

²alias -gグローバルzshエイリアスが表示されますが、これらのエイリアスは次の領域だけでなくグローバルにも拡張されます。コマンド位置

関連情報