このコマンドは、単独で実行されたときに予想される結果を生成します(crontabの最後の行)。
tail -n 1 /etc/crontab
ただし、結果をファイルに送信するためにechoコマンドの一部として実行すると、期待される結果とともに、作業ディレクトリ内のすべてのファイルの要約が追加されます。
sudo bash -c 'echo $(tail -n 1 /etc/crontab) > /path/to/file'
このコマンドが追加データを生成するのはなぜですか?
答え1
crontab行*
に「いつでも」を意味する1つ以上のアスタリスクがあります。コマンド置換で行を置き換えると、結果は次のようになります。
echo * * * * * cmd > /path/to/file
しかし、最大コマンド代替出力には追加の拡張は適用されません。パス名拡張はい(フィールド分割と同じ):
追加のチルダ拡張、パラメータ拡張、コマンド置換、または算術拡張のためにコマンド置換結果を処理しないでください。二重引用符内でコマンド置換が発生した場合、フィールドの分割とパス名拡張交換結果に対して実行しないでください。
パス名拡張は、すべてのエントリに一致*.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'
これは必要な操作を実行する必要があり、より簡単です(他の唯一の実質的な違いは、このバージョンで発生するフィールド分割を省略するため、スペースが縮小されないことです)。
答え2
次のファイルを含むディレクトリを考えてみましょう。
$ 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*
二重引用符がない場合、結果は次のようになります。コマンドの置き換えシェルに拡張されました。拡張の一つはパス名拡張。上記の場合、これはf*
一致するように拡張することを意味しますf
。
シェル拡張を明示的に望まない限り、すべてのシェル変数および/またはコマンド置換を二重引用符で囲みます。
答え3
*
globbing shell メカニズムはローカルファイルに拡張されます。
crontab 行に*
as プレースホルダがある場合があります。
たとえば、crontabのこの行は日曜日の午前7時47分に実行され、最初の星は日付を表し、2番目の星は月を表します。
47 7 * * 0 /run/on/sunday
それからあなたtail
と問題
echo 47 7 * * 0 /run/on/sunday
*
ローカルファイルに展開されます。