1. ホーム
  2. スクリプト・コラム
  3. DOS/BAT

Windowsのcmdコマンドラインの入出力リダイレクト問題

2022-02-07 12:19:32

NAT後の総輸出帯域はわずか4MB/s(出典:360スピードテスト)、残念ですが、この帯域を大勢で共有するというのは、実際に体験しないとわかりませんね。
"よくわからないんだけど。

リダイレクト記号は、>, >>, <, >&, <&, <& および | です。以下では最初の5つだけを説明し、最後の1つはパイプで、これもUnixと同じです。

セクション1

古典的な質問から始めると、"1>nul 2>nul" は通常出力とエラー出力の両方をマスクすることを意味するので、ここでいう1とは通常出力(いわゆる "standard output" -- stdout)、2とはエラー出力(いわゆる "standard error output" -- stderr)だということはすぐにわかりますね。--stdout)、2がエラー出力(いわゆる"標準エラー出力" --stderr)です。

1と2は、実際にはstdoutとstderrというハンドルの数値指定です。ハンドルとは何かというと、何かの識別子、あるいは何かへのハンドルと理解すればよいと思います。例えば、標準出力はstdoutというハンドルで識別されますし、stdoutというハンドルは標準出力を指しています。
また、stdinというハンドルがあり、これはいわゆる"標準入力"を数字指定で0と識別します。 そのほか、3~9までありますが、定義されていません。

"標準出力"と"標準エラー出力"はデフォルトでコンソールコン(=cmdウィンドウ)に出力され、"標準入力"はコンソールコンから入力されます。 リダイレクトの目的は、入出力ストリームをデフォルト位置から新しい位置へリダイレクトすることにあります。シンボル ">" および ">>" のデフォルトのハンドル指定は 1 であり、一方 "<" のデフォルトのハンドル指定は 0 であります。

"echo hhhhhh"のような文がおなじみですが、これはあくまでデフォルトの状態であって、それ以上のものがあるのです。完全なステートメントは "echo hhhhhh 1>con 2>con" のようになり、echo コマンドの標準出力と標準エラー出力がコンソール con に出力されますが、標準エラー出力は空であることを除いて、このステートメントが意味するのは、このようなことです。

別の例として、dir に "dir /mm" のようなエラー引数を与えた場合、標準出力が空になることを除いて、全体を "dir /mm 1>con 2>con" のように書きます。もし "dir /mm 1>hero.txt" と書くと、エラーメッセージは通常通り画面に表示されますが、hero.txtには何も内容がありません。

標準入力の別の例として、"set /p var=" は本当は "set /p var= 0<con" のように見えるはずですが、0< con はデフォルト値で省略することが可能です。もちろん、"set /p var= 0<file.txt" のように、ファイルから入力を読み取ることもできます。

nul は "empty device" の略で、存在しないデバイスであり、出力ストリームを空のデバイスにリダイレクトすることはブロックアウトすることと同じです。また、空のデバイスから入力を読み込む場合、当然何も読み込まないのですが、それは入力であり、そのため "set /p var=<nul" の "<nul" は、改行はしないがキャリッジリターンと等価です。

第2項

次に、ハンドルコード間のリダイレクトについて説明します。前にも述べたように、ハンドル数字コードは3から9までの7つありますが、これらは何をするものなのでしょうか?正直なところ、基本的に役に立たないので、この部分をどうしても知りたいという人以外は読まない方がいいと思います。

"echo hero 1>hero.txt" という行は、標準出力をファイル hero.txt にリダイレクトします。これは、ハンドル指定子 1 を con から hero.txt に変更することと同じです。 "echo hero 3>hero.txt 1<&3" の結果は、ファイル hero. txtが生成され、その内容はheroであり、その処理は以下の通りです: "3>hero.txt"は、ハンドル数値コード3のポインティングを"空の" から hero.txt に変更し、 "1< & 3" はハンドル数値コード3が指しているところを1へコピー、それが hero.txt を指しているので標準出力を hero.txt にリダイレクトしているのです。

"i<&j" と "i>&j" はいずれも、jのポインティングをiにコピーする効果があります。行 "echo hero >hero.txt 2>&1" は、1のポインティングを hero.txt にコピーする効果があります。amp;1" は、標準出力と標準エラー出力が両方とも特定のプロセスである hero.txt にリダイレクトされることを意味します。1のポインティングをconからhero.txtへ、"2>&1"は1が2へ、2がhero.txtへポインティングをコピーすることで、1がコピーされます。このとき、2のポインティングもhero.txtに変更されるので、1も2もhero.txtにリダイレクトされることになります。注:1 は、記号 ">" のデフォルトのハンドル数字指定です。
もう一度 "echo hero 3>hero.txt" を見てみると、なぜこれは結果をファイルにリダイレクトしないのでしょうか?リダイレクトしたいのは標準入力、標準出力、標準エラー出力だけで、実際に機能するのは0、1、2の3つだけです。

"more 3<hero.txt 0>&3"という文は、hero.txtというファイルを示しており、"3<hero.txt"という処理は3の指す先を hero.txt に変え、 "0>&3" というのは0の指し先を hero.txt にコピーします(今回だけ hero.txt からのデータ読み込みです)。強調したいのは、実際にデータを読むことができるのは3ではなく0であり、3は単なる中間量に過ぎないということです。もちろん、"more 0<hero.txt" や "more <hero.txt" と記述することも可能です。

別の例として、"echo hero 5>hero.txt 4>&5 3<&4 1<&3" とすると hero.txt が出力されることになります。具体的な処理としては、5のポインティングがhero.txtになり、"4>&5"が5のポインティングを4にコピー、"3>&4"が4のポインティングを3にコピー、"1& gt;&3"、 3のポインティングを1にコピー、最後の1が hero.txt なので1によって参照されていた標準出力は hero.txt に振り分けられることになります。

セクション3

注:このセクションは、以下の例題がすべてです。

コード

sudo gedit~/.bashrc

どうなっているんだ?なぜこんなに予想外の結果になったのでしょうか?
これは、ハンドル・デシグネーターのポインティングを変更する前に、そのハンドル・デシグネーターの元のポインティングを、これまでの最初の空のハンドル・デシグネーターにバックアップすることを意味します。その目的は、行が終了したときに、システムがバックアップを使用して元のポインタを取り出すことができるようにするためです。

( 読み進める前に、紙とペンを用意して、混乱しないように個々のハンドル指定子の変更を記録しておくことをお勧めします)
では、"echo.の文に注目してみましょう。1>nul 3>nul" とします。この文は、具体的にどのように動作するのでしょうか?

ステップ1: "1>nul" を実行する前に、1はconのデフォルト値を指しており、コード3から9のポインタはすべて空(初期値)なので、システムは1から3までのポインタをバックアップし(3は最初の空コードなので)、3はconを指しています。つまり、システムは1から3の元のポインタをコピーして、文の最後に元のポインタを取り出せるようにしているわけで、これはバックアップに相当する。

ステップ2:今度は3がconを指していますが、"3>nul"を実行しているので、3の指す先をバックアップしておく必要があります。つまり、3は4でバックアップされていることになります。

ステップ3:"3>nul"で3がnulを指すようになるので、3がnulを指すようにします。

ステップ4:この行の最後に、1は元のポインティングを取り戻したいと思っています。しかし、この時点で3はnulを指しているので、1はnulを指していることになります。

ステップ5:そして3は、元のポインティングを復元するために4を見つけなければならない。4はconを指しているので、3はconに復元されます。4はもともとnullを指していて、そのバックアップが5にあるので、4はnullに復元されます。

では、これを合理的に説明しましょう。今、1はnulを指し、2はデフォルト値のconを指し、3はconを指し、4はそれ以降すべて空のポインティングになっています。つまり、最後の2つのecho文を実行すると、1がnulを指しているため表示がマスクされ、標準出力は空のデバイスにリダイレクトされるのです。

もう一度、 "echo 1>con 4>con" という文はどのように機能するのでしょうか。

ステップ1:1は現在nulを指しており、"1>con"を実行しているのでバックアップされている必要があります。しかし、この時点では3がcon nonemptyを指しているので、システムは1が4を指しているのをバックアップし、4がnulを指していることにします。

ステップ2:ここでも "4>con" を実行しているので、4はnulを指していて5にバックアップされているので、5以降をスキップします。
ステップ3: "4>con" を実行した後、4はconを指します。
ステップ4:このプログラムの行の後、1はポインティングを再開するために4をパスしなければなりません。4はconを指すので、1はconを指し、こうしてデフォルトの状態を再開します。そして、4は5を探し、5はnulを指すので、4はnulを指す。
今いる場所をもう一度数えてみましょう、0点がコン、1点がコン、2点がコン、3が動いていないかコン、4点がヌル、5点がその後にヌルになります。
ちょっとわかりにくいと思いませんか?それなら、もう何度かよく見てください。あるいは、もう少し基本的なことですが、こちらも見てみてください。

第4節
"echo hero" が "echo hero 1>con 2>con" と等価であることは既に知っています。con はコンソールを意味し、con を特別なファイルと考えることができます。これが con という名前のファイルを作ることができない理由です。

もうひとつ、">"と">"のリダイレクト機構を解析してみましょう。リダイレクト先のファイルに隠し属性やシステム属性がある場合、">>"は正常に動作しますが、">"は正常に動作しないことがわかりました。したがって、">"のリダイレクト出力では、ファイルが存在しない場合はもちろんファイルを作成し、ファイルが存在する場合は、まずファイルを削除してから新しいファイルを作成する、つまり、ファイルの内容を上書きするのではなく、まずファイルを削除してから新しいファイルを作成すると推定しています。

なぜ "echo hero >nul >con >hero.txt >con" のような文が最後にかかるかは、第3章の説明で理解できるはずです。

最後の注意点として、リダイレクトされた出力は、読み取り専用のファイルには出力されません。

終了。
WINでのリダイレクトのほとんどは、やはりUnixと一致している、うーん

以下は追加部分です。

Windowsにおけるcmdの標準入出力リダイレクト機能

<テーブル コマンド 機能 コマンド > ファイル名 標準出力をファイルにリダイレクトする コマンド >> ファイル名 標準出力をファイルにリダイレクト(追記)する コマンド 1 > フィールド名 標準出力をファイルにリダイレクトする コマンド > ファイル名 2>&1 標準出力を標準エラーとともにファイルにリダイレクトする コマンド 2 > ファイル名 標準エラーをファイルにリダイレクトする コマンド 2 >> ファイル名 標準エラーをファイルにリダイレクト(追記)する コマンド >> ファイル名 2>&1 標準出力を標準エラーとともにファイルにリダイレクトする(append)。 コマンド < ファイル名1 > ファイル名2 コマンドは、filename1 ファイルを標準入力、filename2 ファイルを標準出力として受け取ります。 コマンド < ファイル名 コマンドは標準入力として filename ファイルを受け取ります。 コマンド << デリミタ デリミタdelimiterに遭遇するまで標準入力から読み込む コマンド < &m ファイルディスクリプタmを標準入力として使用 コマンド > &m 標準出力をファイルディスクリプタにリダイレクトする m コマンド < &- 標準入力を閉じる

以上、Windowsのcmdコマンドライン入出力リダイレクト問題の詳細を説明しましたが、cmdコマンドライン入出力リダイレクトの詳細については、スクリプトハウスの他の関連記事にもご注目ください!