web-dev-qa-db-ja.com

"ls | wc -l"が現在のディレクトリの正しいファイル数を表示するのはなぜですか?

現在のディレクトリにあるファイルの数を数えようとしたところ、ls -1 | wc -lが見つかりました。つまり、ファイルのリスト(すべてのファイル名が新しい行に出力されます)をwcの入力に送信します。ここで、-lは入力の行数をカウントします。意味あり。

私は単にls | wc -lを試してみることにしましたが、正しい数のファイルが得られることにも非常に驚きました。オプションが指定されていないlsコマンドはファイル名を1行に出力するので、なぜこれが発生するのでしょうか。

42
Kirill

info lsから:

「-1」
'-format = single-column'

1行に1つのファイルをリストします。 これは、標準出力が端末でない場合の「ls」のデフォルトです。

lsの出力をパイプすると、1行に1つのファイル名が表示されます。
lsは、出力が人間の目を宛先とする場合にのみ、列でファイルを出力します。


ここでlsが何をすべきかを決定します:

  switch (ls_mode)
    {
    case LS_MULTI_COL:
      /* This is for the 'dir' program.  */
      format = many_per_line;
      set_quoting_style (NULL, escape_quoting_style);
      break;

    case LS_LONG_FORMAT:
      /* This is for the 'vdir' program.  */
      format = long_format;
      set_quoting_style (NULL, escape_quoting_style);
      break;

    case LS_LS:
      /* This is for the 'ls' program.  */
      if (isatty (STDOUT_FILENO))
        {
          format = many_per_line;
          /* See description of qmark_funny_chars, above.  */
          qmark_funny_chars = true;
        }
      else
        {
          format = one_per_line;
          qmark_funny_chars = false;
        }
      break;

    default:
      abort ();
    }

ソース: http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c

57
glenn jackman

lsの出力はstd出力に依存するため、ターミナルとパイプでは異なります。試す

/bin/ls | cat
14
jimmij

歴史的に、lsは1行に1つのファイルを出力しました。これは、他のテキストベースのUnixツール(wcなど)で処理するのに便利な形式です。ただし、スクロールバックのない24行の端末では、大きなリストを表示すると画面がスクロールオフされる傾向があり、探していたものを見つけるのが困難になりました。そのため、ある時点で、BSD開発者は 動作を変更しました なので、端末に出力するときに、lsは出力を複数の列にフォーマットします。既存のシェルスクリプトを壊さないようにするためにパイプまたはファイルに書き込むとき、以前の動作は保持されました-そして、wcのようなコマンドで出力を処理するとき、古い動作はより便利です。複数列の出力をlsに組み込み、それを端末のデフォルトにするという決定は、 行使されたRob Pikeはかなり ; Research Unixは、第8版(BSDに直接基づいていた)まで新しい機能を採用しませんでした。プラン9は、lsをインタラクティブな使用のために、lcを別のコマンドに戻しました。lcは、lsを呼び出すシェルスクリプトと、mc複数列の出力。

-1および-Clsのオプションは、少なくとも出力先に関係なく、ユーザーが特定の出力フォーマットを強制できるようにすることで、正気を取り戻すための遅れた試みです。

10
Jonathan Cast

なぜ「ls | wc -l”は、現在のディレクトリ内の正しい数のファイルを表示しますか?

まあ、それはそこに間違った前提です。それはしません!これを試して:

mkdir testdir
cd testdir
# below two lines are one command, the newline is quoted so will be part of argument
echo text | tee "file
name"
ls -l
ls | wc -l

その最後の行の出力は2です。

ls -lコマンドでコンソールに出力する場合、lsは改行をそのままではなく、?を出力することに注意してください。しかし、これはlsの特別に実装された機能であり、出力が実際の端末に送信されていることを検出すると、おかしなファイル名が端末をめちゃくちゃにしないようにします。これと同じ検出により、ファイル名が1行に1つ(パイプで)印刷されるか、端末の幅に従って印刷されるかが決定されます(これは明らかにis幅のある端末の場合にのみ意味があります)。改行で区切られた生のファイル名を印刷したい場合は、ls | catのようなコマンドでlsをだますことができます。

wc -lは行数を数えるだけで、ファイル名に改行が含まれている場合でも、wcは2行として数えます。


lsには、制御文字-q/--hide-control-charsを強制的に非表示にするスイッチもあります。したがって、ls -q | wc -lは、実際にlsによってリストされたファイルの正確な数を提供する必要があります(これは通常、-aスイッチがない場合、ディレクトリ内の実際のファイル数とは異なります)----- ls出力の改行のみがファイル名を区切るためです。

7
hyde