`bash -c "$cmd"`がここで現在の日付を返すのはなぜですか?

`bash -c "$cmd"`がここで現在の日付を返すのはなぜですか?

コンテキスト:https://stackoverflow.com/a/372​​ 53784/15603477

cmd='printf "%s\n" "$(date -d "$variable" +%c)"'
echo ${variable}

返品2010-09-08 12:34:56

echo "$cmd"返品printf "%s\n" $(date -d "$variable")
echo "${cmd}"返品printf "%s\n" $(date -d "$variable")

bash -c "$cmd" 次に、回答に示すように次のコマンドを実行します。

Fri
Dec
24
00:00:00
IST
2021

見つかった内容は次のとおりです。

オプションではなく、最初の引数command_stringからコマンドを読み取り、実行して終了します。 command_stringの後に引数がある場合、最初の引数は$ 0に割り当てられ、残りの引数は位置引数に割り当てられます。 $ 0の指定は、警告メッセージとエラーメッセージに使用されるシェルの名前を設定します。


私の質問は最後のコマンドbash -c "$cmd"、出力についてです。今日(現在2021年12月24日です。)どこから来ましたか?

--更新:確認しました。https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#evalhttps://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Bourne-Shell-Builtinshttps://man7.org/linux/man-pages/man1/bash.1.html 次の引用は私が見つけたものです。eval

eval [arg ...]引数を読み取り、それを単一のコマンドで連結します。その後、シェルはコマンドを読み取り、実行し、その終了ステータスはeval値として返されます。もし

答え1

シェルコードは3つのパラメータを使用してコマンドをbash -c "$cmd"実行します。bash

  1. bash
  2. -c
  3. $cmd可変内容

bashこれらのシェルを呼び出すことは、3番目の引数のコードを解釈する標準的な方法です(ただ1以外のすべてのシェルがこれを行います)。

$cmdここでは、次のように初期化されると言います。

cmd='printf "%s\n" "$(date -d "$variable" +%c)"'

ただし、出力には次echo "$cmd"のように割り当てられたものが表示されます。

cmd='printf "%s\n" $(date -d "$variable" +%c)'

したがって、新しい呼び出しによって解釈されるシェルコードは次bashのようになります。

printf "%s\n" $(date -d "$variable" +%c)

これでbash新しい通訳が始まるので、$variableそこには割り当てられません。代わりにbash -uc(と同じbash -o nounset -c)を実行すると、-c次のようになります。

bash: line 1: variable: unbound variable

このnounsetオプションがないと、設定されていない変数は空のように拡張されるため、date次の引数を使用してコマンドを呼び出します。

  1. date
  2. -d
  3. 公理
  4. +%c

-d標準dateオプションではありません。 GNUの場合、date入力日を指定するために使用されます。入力日が空の場合、date -d 0またはdate -d 00:00:00、つまり今朝00:00:00と同じです。

$(date...)参照がなく、リストコンテキスト(コマンドprintf引数)にあるため、分割+globの影響を受けます。分割は空白、タブ、および改行文字を含むbashのデフォルトの日付出力を中断するため、出力でスペースで区切られた各単語に対して生成された別々の引数を見ることができます$IFS(ワイルドカード部分はここで何もしません)実行しません)出力にワイルドカード文字はありません。printfdatedate

したがってdate、次のように出力すると、次のパラメータを含む呼び出しがFri Dec 24 00:00:00 IST 2021発生します。printf

  1. printf
  2. %s\n
  3. Fri
  4. Dec
  5. 00:00:00
  6. IST
  7. 2021

すべての引数を使用するためにできるだけ多くの形式をprintf再利用し、最終的にすべての引数を別々の行に印刷します。%s\n

bash今この新しいインスタンスが必要な場合継承するこれを呼び出すシェルの場合、後で呼び出す各コマンドの環境にこの変数を追加でき$variableます。export

export variable
bash -c "$cmd"

printenv variableperl -E 'say $ENV{variable}'…も見ることができます)。

またはbash³ を一度だけ呼び出すには:

(export variable; exec bash -c "$cmd")

または:

variable=$variable bash -c "$cmd"

存在する

eval "$cmd"

現在、シェルには解釈のコードが渡され、$cmd呼び出し内でディスパッチされたシェルなので、$variableその内容にアクセスできるため、環境にエクスポートする必要はありません。


¹これは、実際にはシェル言語と見なすことができない他の言語の一部の通訳者にも当てはまります。pythonたとえば、一部は他のオプションを使用します(たとえば、、、sed -e 'sed code'... perl -e 'perl code'php -r 'php code'またはまったくオプションなし)awk 'awk code'sed 'sed code'

²と$0はに設定されておりbash、ここの位置パラメータのリストは空です。

bash3およびインスタンスが独自に実行するコマンド

答え2

この変数で定義されたコマンドは、名前付き$cmd変数を参照します$variable$cmd他のプロセスの新しいシェル呼び出しに渡されると、$cmdこの変数は実行時に設定されません。したがって、日付インポート呼び出しの結果は、date -d "" +%c現在の日付を返すことです。

コーディングの観点から、コマンドを変数に格納するのは悪い習慣です。あなた〜する近いうちに、引用の悪夢に陥るでしょう。変数をデータとして、関数をコードとして使用してください。

cmd() {
    local v=$1
    printf "%s\n" "$(date -d "$v" +%c)"'
}

variable="2010-09-08 12:34:56"
cmd "$variable"

最後に、妥当な理由がない限り、変数を使用するときは常に二重引用符で囲む必要があります。 (echo $variable悪い、echo "$variable"より良い、printf "%s\n" "$variable"最高。)

関連情報