zshの場合
rm foo.bar
プリントrm: foo.bar: No such file or directory
。rm foo.bar 2>/dev/null
予想どおり、何も印刷されません。
ただし、コマンドにパターン マッチが含まれている場合、エラーは次のように抑制されません2>/dev/null
。
rm *.bar
プリントzsh: no matches found: *.bar
。rm *.bar 2>/dev/null
同じものが印刷されます。
zshでエラーメッセージを抑制する一般的な方法はありますか?単純あらゆる種類のエラー メッセージ用のメソッド。
答え1
実行しようrm *.bar
としたときに一致するファイルがない場合*.bar
、基本的に次の 2 つのことが起こる可能性があります。
POSIX の動作では、シェルは
rm
literal を*.bar
引数として実行します。ツールは、*.bar
( を実行した場合のようにrm '*.bar'
) 文字通りに名前が付けられたファイルを削除しようとしますが、失敗して のようなものを出力しますrm: *.bar: No such file or directory
。これが、POSIX シェル (sh
) および互換シェルの動作です。POSIX以外の動作では、シェルは一致がないことを検出し、次のようなものを出力して
no matches found: *.bar
実行しません。rm
まったく. これは、Zsh のデフォルトの動作です。 (比較: Bash では、 によってこの動作に切り替えることができますshopt -s failglob
。)
(前者の場合)からのエラーはrm
の stderr に出力されますrm
。 (後者の場合) シェルからのエラーは、シェルの stderr に出力されます。
のリダイレクトは+rm *.bar 2>/dev/null
の stderr にrm
のみ影響します。あなたの例では実行すらされません。rm
メイン シェルの stderr をリダイレクトするには、次のものが必要ですexec
。「メイン シェル」とは、入力する対話型シェル、またはスクリプトを実行する場合はスクリプトを解釈するシェルを意味します。例:
exec 2>/dev/null
Zsh では閉じることもできます:
exec 2>&-
合理的なアプローチは、後で使用(または復元)する必要がある場合に備えて、元のstderrを事前に複製することです。例(このコードを対話型Zshに貼り付ける場合は、次のように呼び出す必要があります。setopt interactive_comments
初め):
exec 7>&2 # "save" stderr
exec 2>/dev/null # redirect
rm *.bar
whatever
exec 2>&7 # restore
exec 7>&- # close descriptor which is no longer needed
これは7
任意の 1 桁の数字で、ファイル記述子として使用されません。最初の 2 行は 1 つに記述できます。exec 7>&2 2>/dev/null
同様に、最後の 2 行も同様に記述できます。
rm
または などのコマンドは、whatever
stderr リダイレクトなしで呼び出され、シェルから stderr を継承することに注意してください。つまり、上記の例ではrm
(実行される場合)、および は、whatever
エラー メッセージ (ある場合) を に出力します/dev/null
。その後、exec 2>/dev/null
任意の数の または コマンドを呼び出すことができ、それらすべてが stderr になります/dev/null
。すべての種類のエラー メッセージを抑制したい場合は、これが方法です。
このソリューションは、Zsh だけでなく、多くのシェルで機能します。対話型シェルの stderr をリダイレクトする場合は、あまり洗練されていないシェルの中には、プロンプトを印刷するために stderr を使用するものがあることに注意してください。
プロセスは自身のstderrをリダイレクトするかもしれない(シェルが でするようにexec 2>…
);またはエラーメッセージをstdoutまたは に出力するかもしれない(そうすべきではないが、技術的には可能である/dev/tty
)。したがって、exec 2>/dev/null
ないエラー メッセージのように見えるメッセージが表示されないことを保証します。
その後もexec 2>/dev/null
、必要に応じて任意のコマンドの stderr をリダイレクトできます。たとえば、代わりに次のようwhatever
にすると:
whatever 2>&7
すると、そのエラー メッセージは、ファイル記述子として意図的に保存した元の stderr に送信されます7
。
exec
rm
は、シェルの stderr (または別のファイル記述子) をリダイレクトする唯一の方法ではありません。のようにシェル (メイン シェル全体ではありません) を扱うことができますrm … 2>/dev/null
。サブシェルは次のように扱うことができます。
( rm *.bar ) 2>/dev/null
または、メインシェルによって解釈されるコードのみ:
{ rm *.bar; } 2>/dev/null
(;
これは Zsh ではオプションですが、他のシェルでは必須です。私は単にコードを Zsh 以外でも動作させたかったのです)。
これらの行のいずれかで問題を解決できます。シェルからのエラー メッセージを抑制するという文脈では、2 つの行は同等です++。
リダイレクトは(
/で始まり/{
で終わり、その範囲は限定されていることに注意してください。それでも、括弧内には多くのコマンドを配置できるため、「限定された」範囲は実際にはスクリプト全体である可能性があります。または、スクリプトの一部だけである可能性もあります (この回答で以前に紹介したものを含め、何でもかまいません)。リダイレクトが/の後で機能しなくなるという事実により、このアプローチは で stderr を保存および復元する代わりになる優れた方法になります。)
}
whatever
)
}
exec
+厳密に言えば、" rm
" は " になる前と後に(または " になろうとした後に)影響しますrm
。我慢してください。 のリダイレクトはrm … 2>/dev/null
、rm
実行可能ファイルに置き換えられようとしているフォークされたシェル. このシェルは、自身を に置き換える前に、自身の stderr をリダイレクトしますrm
。リダイレクトの実行後に報告されるエラーはすべて に送られます/dev/null
。たとえば、rm
が見つからない場合は、すでに実行されたリダイレクトによってcommand not found
エラーが抑制されます。
++技術的には( … )
サブシェルを作成しますが、{ … }
作成しません。サブシェルは、変数と現在の作業ディレクトリを継承する別のシェルプロセスのように動作しますが、親シェルの変数や現在の作業ディレクトリなどを変更することはできません。本当に別のプロセスとして実現される場合もそうでない場合もありますが、重要なのは動作です。一方、{ … }
何らかの目的 (この場合、目的はリダイレクト) のためにコマンドをグループ化するだけです。これは、 ; を使用するときにサブシェルが存在しないという意味ではありません{ … }
。たとえば、パイプラインでは、最後の部分を除くすべての部分がいずれにしてもサブシェルです (一部のシェルでは、最後の部分を含むすべての部分)。これらの技術的な詳細は、私たちの問題のコンテキストだけでは無関係ですが、一般的には違いを生む可能性があります。