【コラム】

漢のzsh

7 ひと味違うzshのリダイレクト

    後藤大地  [2007/03/22]

    リダイレクトはシェルに欠かせない機能の一つだ。標準出/入力をファイルへ向けたり、標準出力を標準エラー出力へ向けたり、また、その逆もできる。リダイレクトはできるシェル使いの第1歩。"command > /dev/null 2>&1"とかっこよくやってみたいもの。

    zshも当然リダイレクト機能を持っている。しかし、zshのリダイレクトはbashやtcshのそれとはひと味違う。zshでは最初からリスト1.1の設定が有効になっており、bashやtcshではデフォルトでは実現されていない機能もサポートしている。今回はzshのリダイレクト機能を紹介しておこう。

    リスト1.1 multiosオプション - デフォルトで有効になっている

    % setopt multios

    リダイレクト先を複数設定

    echoコマンドでテキストを出力して、リダイレクトさせてファイルへ書き込む。これはシェル操作の最初の一歩だが、zshではこれをプロンプト2.1のように書ける。tcshでは「> file1 > file2」はAmbiguous output redirectとしてエラー扱いになって処理されないし、bashではfile2の方にだけテキストが書き込まれる。zshだと両ファイルともに同じテキストが書き込まれる。

    プロンプト2.1 zshのリダイレクトは2つ以上指定可能

    % ls -l
    total 0 
    % echo "welcome to the new land" > file1 > file2
    % cat file1
    welcome to the new land
    % cat file2
    welcome to the new land
    %

    ちなみにzshの場合、catコマンドを使わずともプロンプト2.2のように記述することでmoreコマンドと同じような処理をさせることができる。当然、zshなら入力のリダイレクトも複数いける。つまりプロンプト2.3のように書くこともできる。

    プロンプト2.2 cat/moreのかわりに「<」で処理してみる

    % < file1
    welcome to the new land

    プロンプト2.3 もちろん入力のリダイレクトも複数指定が可能 

    % < file1 < file2
    welcome to the new land
    welcome to the new land

    プロンプト2.3は通っぽい書き方ではあるが、環境変数や設定で動作がかわるしパイプの後では使えない。そもそも一目見て意味わかりにくいので、一通り遊んだあとはいつもどおりコマンドを書く方法を使った方がいいかも。ただし、ここぞというときにおとなりと差をつけられるかも?

    パイプとも連携するリダイレクト

    zshのすばらしい点はこれだけにとどまらない。パイプを組み合わせてもっとアグレッシブなことができる。たとえばプロンプト3.1をみてほしい(コマンド行先頭の「<」はcatコマンドのかわりで使っている。せっかくなのでこの書き方にしてみたが、ここはcatにしてもいいだろう)。file1の内容をfile3にコピーしているわけだが、これをプロンプト3.2のように記述すると、標準出力にもfile1の内容が出力されるのだ。

    プロンプト3.1 file1の内容をfile3へコピー

    % < file1 > file3             
    % < file3
    welcome to the new land

    プロンプト3.2 file1の内容をfile3へコピーしつつ、内容を標準出力へも表示

    % < file1 > file3 | cat
    welcome to the new land

    プロンプト3.2は、bashなら標準出力にはなにも出力されないし、tcshならAmbiguous output redirectになってエラー扱いで処理すらされない。しかしzshならパイプもリダイレクトのように複数入出力の対象になる。これは使いだすと結構便利だったりする。

    ちなみにパイプではなく、リダイレクトで同じこともできる。プロンプト3.3のようにリダイレクト先に/dev/stdinを追加すればいい。

    プロンプト3.3 パイプでcatコマンドへつなげるかわりに、/dev/stdinへリダイレクトを追加するとか

    % cat file1 > file3 > /dev/stdin
    welcome to the new land

    この処理は、コマンドから出力されるメッセージをファイルに落としながらもターミナルにも流しておきたいという場合によく利用するteeコマンドと同じだ。zshの場合、teeコマンドを使わなくともリダイレクトやパイプを組み合わせるだけで実現できるというわけだ。

    teeコマンドと同じ処理をするくらいならteeコマンドを使えばいいわけで、このマルチリダイレクトがその本領を発揮するのは複数のリダイレクトをやりたいというシーンだろう。シェルスクリプトで複数のリダイレクトをつかって変態的ワンライナーを作るとことが簡単に進むことがある。shでは難しいこのシチュエーションも、zshを使ってしまえば問題ない。

    ファイルグロブとリダイレクト

    zshのリダイレクトはファイブグロブとも連系している。たとえばプロンプト4.1のように書いた場合、file1、file2、file3の全てに"hello world"を書き込みたいという気持ちは伝わってくるものの、実際にはfile1に"hello world file2 file3"が書き込まれるだけで、file2とfile3には何もおこならない。

    プロンプト4.1 気持ちは伝わってくるが、これではfile1にしかメッセージは書き込まれない

    % echo "hello world" > file1 file2 file3
    % < file1
    hello world file2 file3
    % < file2
    welcome to the new land
    % < file3
    welcome to the new land

    しかし、これをファイルグロブを使ってプロンプト4.2のように実行すると、グロブで展開されるファイルすべての対して"hello world"が書き込まれる。つまりecho "hello world" > file?が"hello world" > file1 > file2 > file3に展開されるわけだ。

    プロンプト4.2 ところがファイルグロブを使うとすべてのファイルに対してリダイレクトされる

    % echo "hello world" > file?            
    % < file1
    hello world
    % < file2
    hello world
    % < file3
    hello world

    当然これは入力へのリダイレクトに対しても同じように機能する。つまり、プロンプト4.3のようにすると当然エラーだが、プロンプト4.4のようにするとすべてのファイルが標準入力になるという寸法だ。

    プロンプト4.3 気持ちはわかるが、やはりこれもfile1にはリダイレクトの対象にならない

    % < file1 file2 file3
    zsh: command not found: file2

    プロンプト4.4 しかしこれもファイルグロブを使うとすべてがリダイレクトの対象になる

    % < file?                
    hello world
    hello world
    hello world

    ちなみに、これまでbashを使ってきたのでこのzshの挙動が許せないという向きには、setopt no_multiosを.zshrcに書き込んでおくという選択肢も残されている。

    新着記事

    特設サイトの情報

      人気記事

      一覧

        イチオシ記事

        新着記事

        特別企画

        マイナビニュースマガジン