Linux使いならlsを使わない日はないだろう。
lsのオプションで大体-lをつける人が多いかもしれないが、
たまにはlsでオプションをつけない奴も実行してみてほしい。
ちょっと不思議な動作をしている。
$ls
foobar1 foobar2 foobar4 foobar6 foobar8 foobardasdadada
foobar10 foobar3 foobar5 foobar7 foobar9
上記のようにlsだけを打てば横にずらずらと書かれる
一方で、おなじくオプションをつけないlsの結果をリダイレクトをすると、ファイルが一つずつ改行されて表示される。
$ls > foobar.txt
$cat foobar.txt
foobar.txt
foobar1
foobar10
foobar2
foobar3
foobar4
foobar5
foobar6
foobar7
foobar8
foobar9
foobardasdadada
$
コンソールに出力されているものと、リダイレクトされるもので内容が変わっている
リダイレクトとパイプが改行で区切られるようになっている
ファイルが一つずつ改行されるモードはオプションでは-1というものがある。
数字の1だ。
ls -1 で実行すると一つずつ改行される。
リダイレクトされた内容はまさにそれと同じものだった。
$ls | hexdump -C
00000000 66 6f 6f 62 61 72 2e 74 78 74 0a 66 6f 6f 62 61 |foobar.txt.fooba|
00000010 72 31 0a 66 6f 6f 62 61 72 31 30 0a 66 6f 6f 62 |r1.foobar10.foob|
00000020 61 72 32 0a 66 6f 6f 62 61 72 33 0a 66 6f 6f 62 |ar2.foobar3.foob|
00000030 61 72 34 0a 66 6f 6f 62 61 72 35 0a 66 6f 6f 62 |ar4.foobar5.foob|
00000040 61 72 36 0a 66 6f 6f 62 61 72 37 0a 66 6f 6f 62 |ar6.foobar7.foob|
00000050 61 72 38 0a 66 6f 6f 62 61 72 39 0a 66 6f 6f 62 |ar8.foobar9.foob|
00000060 61 72 64 61 73 64 61 64 61 64 61 0a |ardasdadada.|
パイプで値を渡してみる。
上記の場合はパイプでhexdumpコマンドに渡して、内容を表示するようにした。
すると、ところどころに0aという値が入っている。
これは改行コードのlfを表している。
つまり、コンソールと、リダイレクト・パイプがそれぞれ内容がちがう出力をしている
lsのソースに答えを求めた
ああだこうだ色々考えても仕方がない。
我らLinuxとそのGnuツール群はオープンソースなのだから、
動作に気になるところがあれば、ソースを見ればいい
githubが正ではないみたいだが、ミラーみたいな扱いとしてgithubでも公開されていた。
https://github.com/coreutils/coreutils
src/ls.c にlsのソースがある。
出力先の判定をしているところを見つけた
僕はC言語を使える人間ではないので、それっぽいところから探していく。
decode_switches関数付近にそれらしい処理を見つけた。
case LS_LS:
/* This is for the 'ls' program. */
if (isatty (STDOUT_FILENO))
{
format = many_per_line;
set_quoting_style (NULL, shell_escape_quoting_style);
/* See description of qmark_funny_chars, above. */
qmark_funny_chars = true;
}
else
{
format = one_per_line;
qmark_funny_chars = false;
}
break;
one_per_lineとは、一行につき一つという意味で、もう一つのmany_per_lineとは一行につき沢山って意味だろう。
one_per_lineは-1オプションをつけたときに有効になるようなので、
まさにファイル一つずつに改行モードを示している
case '1':
/* -1 has no effect after -l. */
if (format != long_format)
format = one_per_line;
break;
つまり以下の部分で、改行を入れるモードの制御を行っているということだ。
if (isatty (STDOUT_FILENO))
{
format = many_per_line;
set_quoting_style (NULL, shell_escape_quoting_style);
/* See description of qmark_funny_chars, above. */
qmark_funny_chars = true;
}
else
isatty (STDOUT_FILENO) という関数で何が行われるのか。
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/isatty.3.html
ここのページでみてみると、
ファイルディスクリプターが端末を参照しているかどうかを調べてくれるらしい。
なんのこっちゃわからない説明だ。
http://x68000.q-e-d.net/~68user/unix/pickup?isatty
こちらのページをみてみると、出力がコンソールかパイプかがわかるという例でほぼ同じコードが載っている。
つまり、isattyでコンソールかパイプかを振り分けて、出力結果を変えているのだ。