web-dev-qa-db-ja.com

テールコマンドをエコーすると、予期しない出力が生成されますか?

このコマンドを単独で実行すると、期待される結果(crontabの最後の行)が生成されます。

tail -n 1 /etc/crontab

ただし、echoコマンドの一部として実行して結果をファイルに送信すると、作業ディレクトリ内のすべてのファイルの概要と予想される結果が追加されます。

Sudo bash -c 'echo $(tail -n 1 /etc/crontab) > /path/to/file'

なぜこのコマンドは余分なデータを生成したのですか?

8
Davidw

Crontab行には、「いつでも」を示す1つ以上のアスタリスク*が含まれています。その行がコマンド置換から置換されると、結果は次のようになります

echo * * * * * cmd > /path/to/file

most以降の展開はコマンド置換の出力には適用されませんが、 パス名展開is(フィールド分割と同様) )

コマンド置換の結果は、さらなるチルド拡張、パラメーター拡張、コマンド置換、または算術拡張のために処理されません。コマンドの置換が二重引用符内で行われる場合、フィールド分割およびパス名展開は置換の結果に対して実行されません。 。

パス名展開は、*.txtを一致するファイル名(グロビング)のリストに変換するもので、*はすべてに一致します。最終結果は、crontab行の*ごとにリストされた作業ディレクトリ内のすべての(非表示ではない)ファイル名を取得することです。


投稿したコードがより複雑なコマンドの代表である場合は、展開を引用することでこれを修正できます。

Sudo bash -c 'echo "$(tail -n 1 /etc/crontab)" > /path/to/file'

しかし、もっと簡単に言えば、echoを完全に失うだけです:

Sudo bash -c 'tail -n 1 /etc/crontab > /path/to/file'

これはあなたが望むことをするはずであり、それも同様に単純です(他の唯一の重要な違いは、このバージョンでは本来なら発生するはずだったフィールド分割が省略されるため、スペースのランが折りたたまれないことです)。

22
Michael Homer

これらのファイルを含むディレクトリを考えてみましょう:

$ ls
crontab  file1  file2  file3
$ cat crontab
f*

次に、tailコマンドを実行してみましょう。

$ tail -n 1 crontab
f*

上記はcrontabの最後の行であり、これは私たちが期待するものです。しかしながら:

$ echo $(tail -n 1 crontab)
file1 file2 file3

二重引用符はこの問題を排除します:

$ echo "$(tail -n 1 crontab)"
f*

二重引用符がないと、コマンド置換の結果がシェルによって展開されます。展開の1つはパス名展開です。上記の場合、これはf*は、fで始まるすべてのファイル名に一致するように展開されます。

シェル展開を明示的に必要としない限り、シェル変数やコマンド置換をすべて二重引用符で囲みます。

5
John1024

シェルメカニズムを展開すると*をローカルファイルに。

crontab行には*任意のプレースホルダーとして。

例えばcrontabのこの行は日曜日の午前7時47分に実行され、最初の星は任意の日、2番目は任意の月を意味します。

47  7 * * 0 /run/on/sunday

次に、あなたはtailを発行し、

echo 47  7 * * 0 /run/on/sunday

*をローカルファイルに。

4
Archemar