とてもわかりにくい Bashのリダイレクションの書き方をとてもわかりやすく説明した『よくわかるリダイレクション・チュートリアル』という良い英語の記事がある。Bashでスクリプトを書く方々がいつもきまって悩む部分をとてもわかりやすく説明してくれる記事だ。しかしこの件について日本語で説明している記事がネット上にほとんど見当たらなかったので、今回大雑把にざっと訳してみた。
翻訳について
当記事は次の記事の翻訳です。
よくわかる!リダイレクションのチュートリアル!Bashハッカーの為のウィキ
利用規約:翻訳者オカアツシはこの翻訳についての著作権を放棄します。また翻訳の間違いが元で発生した損失について一切の責任を負いません。利用を以って当規約に同意としたものとみなします。
よくわかる!リダイレクションのチュートリアル!Bashハッカーの為のウィキ
利用規約:翻訳者オカアツシはこの翻訳についての著作権を放棄します。また翻訳の間違いが元で発生した損失について一切の責任を負いません。利用を以って当規約に同意としたものとみなします。
初めに
このチュートリアルはリダイレクションの完全なガイドではありません。ヒヤドキュメントや名前付きパイプなどについては一切説明しません。ただ僕は単に例の 3>&2 とか 2>&1 とか 1>&3- とかそういう奴が何をしているのかを理解するお手伝いをしたいと思うだけです。stdin, stdout, stderrとは
Bash がスタートすると通常3つのファイル記述子が作られます。それは一般的に 標準入力・標準出力・標準エラーと呼ばれています。たとえば LinuxターミナルエミュレーターでBashを実行すると次のように表示されるでしょう。# lsof +f g -ap $BASHPID -d 0,1,2 COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME bash 12135 root 0u CHR RW,LG 136,13 0t0 16 /dev/pts/5 bash 12135 root 1u CHR RW,LG 136,13 0t0 16 /dev/pts/5 bash 12135 root 2u CHR RW,LG 136,13 0t0 16 /dev/pts/5
この
/dev/pts/5
は擬似ターミナルです。Bash は この擬似ターミナルから標準入力を読み取り stdout/stderrを介してこの擬似ターミナルに出力します。 --- +-----------------------+
standard input ( 0 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard output ( 1 ) ---->| /dev/pts/5 |
--- +-----------------------+
--- +-----------------------+
standard error ( 2 ) ---->| /dev/pts/5 |
--- +-----------------------+
コマンド・複合コマンド・サブシェルなどが実行されると、これらのファイル記述子が継承されます。 たとえば
echo foo
を実行すると foo
というテキストが /dev/pts/5
に送り込まれます。これは何故かというと、このコマンドを実行したシェルの標準出力 が /dev/pts/5
に接続しているからです。実行されたコマンドはこのファイル記述子1を /dev/pts/5
に接続した状態を継承するわけです。
単純なリダイレクション
出力のリダイレクション "n> file"
>
は最もシンプルなリダイレクションでしょう。echo foo > file
このコマンド直後に置かれた
> file
が echo
コマンドのファイル記述子を変化させます. これがファイル記述子 1
を file
に繋がる様に変化させるわけです。 (> file
は
1>file
と同じ) するとこうなるでしょう。--- +-----------------------+ standard input ( 0 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| file | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| /dev/pts/5 | --- +-----------------------+
すると今しがた
echo
によって標準出力へ送り込まれた文字は最終的に file
というファイルに流れ着きます。同じように command 2> file
は標準エラー出力を変化させ file
に流れ着くように変化させるわけです。 では command 3> file
は何をするのでしょうか。これは新しいファイル記述子を作成してfile
につなげるわけです。これらの初期化が終わったあとで、コマンドが実行されます。--- +-----------------------+ standard input ( 0 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ new descriptor ( 3 ) ---->| file | --- +-----------------------+
このコマンドはこの新しいファイル記述子に何をするのでしょうか。それは状況によります。しばしば何もしません。以下で何故この新しい記述子が必要になるのかを見ていきましょう。
入力のリダイレクション "n< file"
もし
この状態でコマンドが標準入力から読み込みを行うと、コマンドはコンソールではなく
command < file
をつかってコマンドを実行したら何が起こるのでしょうか。これは実はファイル記述 0
を変化させます。実行結果は次の様になります。--- +-----------------------+ standard input ( 0 ) <----| file | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| /dev/pts/5 | --- +-----------------------+
この状態でコマンドが標準入力から読み込みを行うと、コマンドはコンソールではなく
file
から入力を行います。 >
と同じ様に、<
も新しいファイル記述子を作ることができます。のちに command 3<file
が何故便利なのかを見ていくことになるでしょう。 パイプについて |
では
これが可能になるのはこれらの接続作業がコマンドが実行される前に実行されているからです。そしてコマンドはこれらの接続済みのファイル記述子を継承しているだけなのです。
|
は何をするのでしょうか。左側のコマンドの標準出力を右側の標準入力につなげます。つまりこれはパイプという名前の新しいファイルを作成して、左側のコマンドの書き込み先に設定し、右側のコマンドの読み込み元に設定する訳です。echo foo | cat --- +--------------+ --- +--------------+ ( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|pipe (read) | --- +--------------+ / --- +--------------+ / --- +--------------+ / --- +--------------+ ( 1 ) ---->| pipe (write) | / ( 1 ) ---->| /dev/pts | --- +--------------+ --- +--------------+ --- +--------------+ --- +--------------+ ( 2 ) ---->| /dev/pts/5 | ( 2 ) ---->| /dev/pts/ | --- +--------------+ --- +--------------+
これが可能になるのはこれらの接続作業がコマンドが実行される前に実行されているからです。そしてコマンドはこれらの接続済みのファイル記述子を継承しているだけなのです。
ファイル記述子について知る
ファイル記述子の複製 2>&1
これまでファイル記述子を作ったりリダイレクトしたりする方法を見てきました。では次にこれを複製するためにどうすればいいかを見ていきましょう。まず最初に見るのはお馴染みの
何故ファイル記述子の複製と呼ぶのでしょうか。それは
次のリダイレクション
つまり得られるファイル記述子は
実際に内部的に見ると、これらのファイル記述子は実はシステムコール
同じ様に書き込みファイル記述子 s に書き込んだ行は ファイル記述子 t にも書き込まれることに
なります。
この文法はとても混乱を招きやすいものです。この矢印を見たら誰でもその矢印の方向がコピー の方向を表している様に感じると思うでしょうが、実はこの方向は逆なのです。これは実は
2>&1
です。これはどういう意味でしょうか。ファイル記述子2
に送り込まれた文字がファイル記述子1
に送り込まれるべき場所に送られる様になります。 command 2>&1
では面白くないので ls /tmp/ doesnotexist 2>&1 | less
を使いましょう。--- +--------------+ --- +--------------+ ( 0 ) ---->| /dev/pts/5 | ------> ( 0 ) ---->|from the pipe | --- +--------------+ / ---> --- +--------------+ / / --- +--------------+ / / --- +--------------+ ( 1 ) ---->| to the pipe | / / ( 1 ) ---->| /dev/pts | --- +--------------+ / --- +--------------+ / --- +--------------+ / --- +--------------+ ( 2 ) ---->| to the pipe | / ( 2 ) ---->| /dev/pts/ | --- +--------------+ --- +--------------+
何故ファイル記述子の複製と呼ぶのでしょうか。それは
2>&1
を実行すると2つのファイル記述子が1つの同じファイルを指す様になるからです。 これのことを『別名(エリアス)』と呼ばないでください。何故かというと 2>&1
を実行して2
をファイル B
にリダイレクトしてもファイル記述子2
は依然としてファイル A
に対して開かれているからです。 これは標準入力と標準出力の両方をリダイレクトしようとする人がしばしば誤解していることです。このことについてもう少し見ていきましょう。仮に2つのファイル記述子 s と tが次のような状態だったとします。--- +-----------------------+ a descriptor ( s ) ---->| /some/file | --- +-----------------------+ --- +-----------------------+ a descriptor ( t ) ---->| /another/file | --- +-----------------------+
次のリダイレクション
t>&s
(但し t
と s
は任意の整数 ) は次のような処理を行います。ファイル記述子 s の内容がなんであれ取り敢えず全部 ファイル記述子 t にコピーしろ
つまり得られるファイル記述子は
--- +-----------------------+ a descriptor ( s ) ---->| /some/file | --- +-----------------------+ --- +-----------------------+ a descriptor ( t ) ---->| /some/file | --- +-----------------------+
実際に内部的に見ると、これらのファイル記述子は実はシステムコール
fopen
によって開かれたものでその正体はただの書き込み/読み込み用のファイルへのポインターです。なお継承する時はファイルの読み込み位置/書き込み位置もあわせて継承されることに注意しましょう。同じ様に書き込みファイル記述子 s に書き込んだ行は ファイル記述子 t にも書き込まれることに
なります。
この文法はとても混乱を招きやすいものです。この矢印を見たら誰でもその矢印の方向がコピー の方向を表している様に感じると思うでしょうが、実はこの方向は逆なのです。これは実は
到着 >& 出発
ということなのです。 つまりやや煩雑ですが次のようなことをするのと同じです。exec 3>&1 # Copy 1 into 3 exec 1> logfile # Make 1 opened to write to logfile lotsa_stdout # Outputs to fd 1, which writes to logfile exec 1>&3 # Copy 3 back into 1 echo Done # Output to original stdout
リダイレクションの順番 "> file 2>&1" vs. "2>&1 >file"
コマンドライン中のリダイレクション指定の場所は特に指定されていませんが、その順番には意味があります。リダイレクションは左から右へ処理されていきます。次の例を考えてみましょう。
するとシェルは
そのとおりです! 何も変わりません! 2は既に1と同じ場所を指しているからです。
次にシェルは
これは期待する動作ではありません!
では次の(正しい)例を見てみましょう。
そして複製
見事に
2>&1 >file
command 2>&1 > file
を stderr
と stdout
の両方に実行することです。これがどういうことなのか見ていきましょう。 ではコマンドを実行してファイル記述子がどうなるか見てみましょう。--- +-----------------------+ standard input ( 0 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| /dev/pts/5 | --- +-----------------------+
するとシェルは
2>&1
を見つけ 1
を複製し、次のようになります。 --- +-----------------------+ standard input ( 0 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| /dev/pts/5 | --- +-----------------------+
そのとおりです! 何も変わりません! 2は既に1と同じ場所を指しているからです。
次にシェルは
> file
つまり stdout
を変更します。--- +-----------------------+ standard input ( 0 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| file | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| /dev/pts/5 | --- +-----------------------+
これは期待する動作ではありません!
では次の(正しい)例を見てみましょう。
>file 2>&1
>file
を見つけます。--- +-----------------------+ standard input ( 0 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| file | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| /dev/pts/5 | --- +-----------------------+
そして複製
2>&1
を行います。--- +-----------------------+ standard input ( 0 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| file | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| file | --- +-----------------------+
見事に
1
と 2
がファイルにリダイレクトされるようになりました。何故 sed 's/foo/bar/' file >file はダメなのか
これもよくある間違いです。同じファイルから読み込んで標準出力に書き込みたい。これを実現するため標準出力を書き換えようとするファイルに変更してしまう。ここでの問題はこれまで見てきたようにリダイレクション処理が実はコマンドの実行が始まる前に行われるからです。
つまり標準出力は sed が始まる前に既にリダイレクトされてしまっているのです。そして同時に > が持っている副作用『書き込む前にファイルを空にする』が実行されます。つまり sedの実行が開始される時、そのファイルは既に空になってしまった後ということになります。
つまり標準出力は sed が始まる前に既にリダイレクトされてしまっているのです。そして同時に > が持っている副作用『書き込む前にファイルを空にする』が実行されます。つまり sedの実行が開始される時、そのファイルは既に空になってしまった後ということになります。
内部コマンド exec 文の使い方
Bash では
例えば
つまり exec は例えばスクリプトファイルで実行する全てのコマンドのエラーをログとしてファイルに保存しようと思ったら スクリプトファイル先頭で
他の用途を見てみましょう。ファイルを一行一行見ていきたいとします。これは簡単です。 次のように実行すればよいのです。
さて次にそれぞれの行を表示したあとで一時停止するように改造したくなったらどうでしょうか。
驚くべきことに、これは正しく動作しません。何故でしょうか。それはwhile 文のなかのシェルのファイル記述子は次のようになっているからです。
そして
ファイル記述子は次のようになります。
これで正しく動作するでしょう。
exec
内部コマンドは、シェルを特定のプログラムによって置き換えるためのコマンドです。このコマンドがリダイレクションにどの様な影響を与えるのでしょうか。実は exec
はファイル記述子を操作する機能があります。もしもプログラムを指定せずに exec
を実行すると exec 以降 シェルのファイル記述子(訳注:デフォルトのリダイレクション)の設定が変更されます。例えば
exec 2>file
を実行したあとの全てのコマンドは次のようなファイル記述子を持ちます。 --- +-----------------------+ standard input ( 0 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| file | --- +-----------------------+
exec 2>file
を実行したあとの全てのエラーは全てのコマンドに 2>file
をつけて実行したように、全てのエラーが stderr にリダイレクトされます。つまり exec は例えばスクリプトファイルで実行する全てのコマンドのエラーをログとしてファイルに保存しようと思ったら スクリプトファイル先頭で
exec 2>myscript.errors
を実行するだけでよいのです。他の用途を見てみましょう。ファイルを一行一行見ていきたいとします。これは簡単です。 次のように実行すればよいのです。
while read -r line;do echo "$line";done < file
さて次にそれぞれの行を表示したあとで一時停止するように改造したくなったらどうでしょうか。
while read -r line;do echo "$line"; read -p "Press any key" -n 1;done < file
驚くべきことに、これは正しく動作しません。何故でしょうか。それはwhile 文のなかのシェルのファイル記述子は次のようになっているからです。
--- +-----------------------+ standard input ( 0 ) ---->| file | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| /dev/pts/5 | --- +-----------------------+
そして
read
はこれらのファイル記述子を継承します。そして(read -p "Press any key" -n 1
)もそれを継承することになります。では help read
を読んで read が読みだすファイル記述子を指定することにしましょう。次の様に exec
使って新しいファイル記述子を作成します。exec 3<file- while read -u 3 line;do echo "$line"; read -p "Press any key" -n 1;done
ファイル記述子は次のようになります。
--- +-----------------------+ standard input ( 0 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard output ( 1 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ standard error ( 2 ) ---->| /dev/pts/5 | --- +-----------------------+ --- +-----------------------+ new descriptor ( 3 ) ---->| file | --- +-----------------------+
これで正しく動作するでしょう。
ファイル記述子を閉じよう
ファイル記述子を閉じるのは簡単です。それを - として複製すればよいだけです。 例えば
このように {} のなかではファイル記述子が 1 しかないことがわかります。もしスクリプトファイルがファイル記述子をごちゃごちゃに開いてしまっても、恐らくOSが綺麗に掃除をしてくれる筈ですが、ファイル記述子を作ったらきちんとそれを閉じたほうが恐らくよいでしょう。何故かというと例えばもし
僕はこれまで stderr を捨てるために
だから僕は可能な限り
stdin <&-
と stderr 2>&-
を閉じてみましょう。bash -c '{ lsof -a -p $$ -d0,1,2 ;} <&- 2>&-' COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME bash 10668 pgas 1u CHR 136,2 4 /dev/pts/2
このように {} のなかではファイル記述子が 1 しかないことがわかります。もしスクリプトファイルがファイル記述子をごちゃごちゃに開いてしまっても、恐らくOSが綺麗に掃除をしてくれる筈ですが、ファイル記述子を作ったらきちんとそれを閉じたほうが恐らくよいでしょう。何故かというと例えばもし
exec 3>file
というファイル記述子を開いたとしたら、それ以降実行する全てのコマンドはそのファイル記述子を継承してしまうでしょう。よってファイル記述子を作ったら次のようにするとよいかも知れません。exec 3>file ..... #commands that uses 3 ..... exec 3>&- #we don't need 3 any more
僕はこれまで stderr を捨てるために
command 2>&-
みたいなことをしている人たちを見たことがあるのですが、あまりよいアイデアではないような気がします。確かにそれでも動くでしょうが、そのあとに実行される全てのアプリケーションがその閉じてしまった stderr に対して正しく動作するかどうかは疑わしいと僕は思うのです。だから僕は可能な限り
2>/dev/null
を使います
実際の例
この例は this post (ffe4c2e382034ed9) ( comp.unix.shell )から持ってきたものです。
リダイレクションは左から右に処理されます。しかしファイル記述子は継承されるため外側から内側へ継承される文脈も追跡しなければいけません。
一番外側の
標準出力と標準エラーを2つコピーします。ここで
続きを見ていきましょう。次は2番めのパイプです。
これは前回のファイル記述子を継承しており、3 と 4 のファイル記述子を閉じて読み込み用のパイプを設定しています。次に左側にある2番めのパイプ
ファイル記述子1はパイプにつながっており、2は1のコピーになっています。 つまり1つのファイル記述子をパイプ (
これは前のファイル記述子を継承し0を最初のパイプに接続します。ファイル記述子3によって2を作成し3を閉じます。最後に左側のパイプを見ていきます。
これもまた左側の2番めのパイプを継承します。ファイル記述子1は最初のパイプに接続され3は閉じられます。
これがもしこれらのコマンドだけであれば、全体の目的はとても明快です。
最初に1 と 2を異なるファイルに設定しましたが、最初に予告した通りに
{ { cmd1 3>&- | cmd2 2>&3 3>&- } 2>&1 >&4 4>&- | cmd3 3>&- 4>&- } 3>&2 4>&1
リダイレクションは左から右に処理されます。しかしファイル記述子は継承されるため外側から内側へ継承される文脈も追跡しなければいけません。
一番外側の
{ } 3>&2 4>&1
を見てみましょう。 --- +-------------+ --- +-------------+ ( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 | --- +-------------+ --- +-------------+ --- +-------------+ --- +-------------+ ( 1 ) ---->| /dev/pts/5 | ( 4 ) ---->| /dev/pts/5 | --- +-------------+ --- +-------------+ --- +-------------+ ( 2 ) ---->| /dev/pts/5 | --- +-------------+
標準出力と標準エラーを2つコピーします。ここで
3>&1 4>&1
だったとしても同じ結果になることに注意しましょう。 何故かというとここではこのコマンドをターミナルから実行するからです。この場合だと 1
も 2
両方共にターミナルにつながっているからです。ここでは練習として 1
を file.stdout
として 2 を file.stderr
として設定することにしましょう。この方がリダイレクションのメカニズムの素晴らしさをよく理解できるからです。続きを見ていきましょう。次は2番めのパイプです。
| cmd3 3>&- 4>&-
--- +-------------+ ( 0 ) ---->| 2nd pipe | --- +-------------+ --- +-------------+ ( 1 ) ---->| /dev/pts/5 | --- +-------------+ --- +-------------+ ( 2 ) ---->| /dev/pts/5 | --- +-------------+
これは前回のファイル記述子を継承しており、3 と 4 のファイル記述子を閉じて読み込み用のパイプを設定しています。次に左側にある2番めのパイプ
{…} 2>&1 >&4 4>&- | です。
--- +-------------+ --- +-------------+ ( 0 ) ---->| /dev/pts/5 | ( 3 ) ---->| /dev/pts/5 | --- +-------------+ --- +-------------+ --- +-------------+ ( 1 ) ---->| /dev/pts/5 | --- +-------------+ --- +-------------+ ( 2 ) ---->| 2nd pipe | --- +-------------+
ファイル記述子1はパイプにつながっており、2は1のコピーになっています。 つまり1つのファイル記述子をパイプ (
2>&1
) につなげています。そして 1 は 4(>&4) のコピーに、そして 4 は閉じられています。これらは内側の {} からのファイル記述子になっています。更に内側に入って右側のパイプを見ていきます。 | cmd2 2>&3 3>&-
--- +-------------+ ( 0 ) ---->| 1st pipe | --- +-------------+ --- +-------------+ ( 1 ) ---->| /dev/pts/5 | --- +-------------+ --- +-------------+ ( 2 ) ---->| /dev/pts/5 | --- +-------------+
これは前のファイル記述子を継承し0を最初のパイプに接続します。ファイル記述子3によって2を作成し3を閉じます。最後に左側のパイプを見ていきます。
--- +-------------+ ( 0 ) ---->| /dev/pts/5 | --- +-------------+ --- +-------------+ ( 1 ) ---->| 1st pipe | --- +-------------+ --- +-------------+ ( 2 ) ---->| 2nd pipe | --- +-------------+
これもまた左側の2番めのパイプを継承します。ファイル記述子1は最初のパイプに接続され3は閉じられます。
これがもしこれらのコマンドだけであれば、全体の目的はとても明快です。
cmd2 --- +-------------+ -->( 0 ) ---->| 1st pipe | / --- +-------------+ / / --- +-------------+ cmd 1 / ( 1 ) ---->| /dev/pts/5 | / --- +-------------+ / --- +-------------+ / --- +-------------+ ( 0 ) ---->| /dev/pts/5 | / ( 2 ) ---->| /dev/pts/5 | --- +-------------+ / --- +-------------+ / --- +-------------+ / cmd3 ( 1 ) ---->| 1st pipe | / --- +-------------+ --- +-------------+ ------------>( 0 ) ---->| 2nd pipe | --- +-------------+ / --- +-------------+ ( 2 ) ---->| 2nd pipe |/ --- +-------------+ --- +-------------+ ( 1 ) ---->| /dev/pts/5 | --- +-------------+ --- +-------------+ ( 2 ) ---->| /dev/pts/5 | --- +-------------+
最初に1 と 2を異なるファイルに設定しましたが、最初に予告した通りに
cmd2
/ cmd3
からの標準入力が元々の標準入力につながり、標準エラー出力が元々の標準エラー出力につながるところを観察することができました。リダイレクションの文法について
僕はかつて
(訳注:中略。興味がある方は各自オリジナル記事を参照のこと。)
0&<3
3&>1
3>&1
->2
-<&0
&-<0
0<&-
みたいなやつを正しく選ぶのにいつも苦労していたのですが、多分それは、記号が結果の状態を表していて実際にどういう処理をしているのかを表していないからではないかと思うのです。例えば「開く」「閉じる」「複製する」等々が書き表されてないと思うんです。で、もし(僕が発見した)このルールがこれを読む人の状況にフィットしていれば、助けなるかなと思うのでここに書き込みます。(訳注:中略。興味がある方は各自オリジナル記事を参照のこと。)
コーディングスタイルについて
(訳注:中略。興味がある方は各自オリジナル記事を参照のこと。)結論
このチュートリアルが読者の助けになることを願っています。
Stéphane Chazelasさんどうもありがとうございます。
僕はサンプルと冒頭の文章を盗みました。
僕はこのイントロに触発されたのですが、ここにいくと更に詳しいサンプルを見ることができます。
1>&3-
について説明するの忘れたのでマニュアル読んで下さい。
Stéphane Chazelasさんどうもありがとうございます。
僕はサンプルと冒頭の文章を盗みました。
僕はこのイントロに触発されたのですが、ここにいくと更に詳しいサンプルを見ることができます。