fishとzsh - プロセス置換などコマンドの書き方の違いあれこれ

普段はfishを使っていて満足だけど、次のようなケースでbash/zshとの違いに戸惑うことがある。

  • Web上の記事からコピペで実行したい時
    • そういえばfishのコマンドがそのまま載った記事はほとんど見ない気がする
  • チーム開発をしていて、他の人がシェアしたコマンドをコピペ実行したい時
  • チーム開発をしていて、自分が使ったコマンドを他の人にシェアしたい時
    • そのまま載せたらbash/zsh環境では動かない時

ほとんどの場合エラーメッセージに答えがあるけど一応メモしておく。
以下のコマンド例は注釈がなければfishを指す。

変数定義

zshではAAA=111と書けるが、fishではsetを使う。

AAA=111
Unsupported use of '='. In fish, please use 'set AAA 111'.

⟩ set AAA 111echo $AAA
111

参考: set:シェル変数の設定・一覧・消去・確認する6活用

RAILS_ENV=test rails consoleみたいな書き方

コマンド実行時だけ変数定義をしたい時、zshと同じような指定はエラーになる。

RAILS_ENV=test ./bin/rails c
Unsupported use of '='. To run './bin/rails' with a modified environment, please use 'env RAILS_ENV=test ./bin/rails…'

エラーメッセージに書いてある通りenvを付ける。

⟩ env RAILS_ENV=test ./bin/rails c
Running via Spring preloader in process 59475
Loading test environment (Rails 5.0.0.1)

&&や||の書き方

fishとzshの違いで良く見るケース個人的1位。
fishでは&&||をサポートしていない。代わりに;and/orを使う。

echo 1 && echo 2
Unsupported use of '&&'. In fish, please use 'COMMAND; and COMMAND'.
fish: echo 1 && echo 2echo 1 ;and echo 2
1
2

# orの例
⟩ cat aa ;or echo 2
cat: aa: No such file or directory
2

ちなみにandを忘れて;だけにすると、コマンドが失敗しても継続されるので注意。

# andがないと継続される
⟩ cat a ; echo 2
cat: a: No such file or directory
2

# andがあれば継続されない
⟩ cat a ;and echo 2
cat: a: No such file or directory

終了ステータスのとり方

$?ではなくて$statusを使う。

echo $?
$? is not the exit status. In fish, please use $status.
fish: echo $?
            ^

⟩ echo $status
0

$(...) - コマンド置換

コマンドの実行結果を他のコマンドに渡す時の書き方。

⟩ cat $(echo out.txt)
$(...) is not supported. In fish, please use '(echo)'.
fish: cat $(echo out.txt)
          ^

⟩ cat (echo out.txt)
this is out

なおfishではバッククォートをサポートしていない。

⟩ cat `echo out.txt`
cat: `echo: No such file or directory
cat: out.txt`: No such file or directory

<(...) - プロセス置換

例えばディレクトリ内に含まれるファイル一覧の差分を取りたいとき。

/Users/kanno/tmp% tree dir1 dir2
dir1
└── a.txt
dir2
└── b.txt

zshでは<()を使うことで一時ファイルを通すことなく比較できる。
参考: 一時ファイルはもういらない - プロセス置換

# これはzsh
/Users/kanno/tmp% diff -u <(ls dir1) <(ls dir2)
--- /dev/fd/11  2018-12-15 05:17:56.000000000 +0900
+++ /dev/fd/12  2018-12-15 05:17:56.000000000 +0900
@@ -1 +1 @@
-a.txt
+b.txt

普段使わないけど、前職でサーバーに入ってサクッと使っている人がいてカッコいいと思った。

同じことをfishでやるにはpsubを組み合わせる。

⟩ diff -u (ls dir1 | psub) (ls dir2 | psub)
--- /var/folders/n5/z3tch4l11q796wz2m5m_6xd80000gn/T//.psub.eIiVpGBx3M  2018-12-15 05:20:18.000000000 +0900
+++ /var/folders/n5/z3tch4l11q796wz2m5m_6xd80000gn/T//.psub.QhISb6ERoH  2018-12-15 05:20:18.000000000 +0900
@@ -1 +1 @@
-a.txt
+b.txt

参考: psub:fish独自のプロセス置換を行う

リダイレクトの書き方

標準エラー出力^を使う。追記する場合は^^

⟩ cat aaa ^out.txt

⟩ cat out.txt
cat: aaa: No such file or directory

存在しないファイルを上書きしないようにするには>?^?を使う。

echo aa >? out.txt
The file 'out.txt' already exists

標準出力と標準エラー出力を同時に出すにはこう書く。

echo aa > out.txt ^&1

絶対忘れるが、bash/zsh2>&1も毎回ググっているので問題ない^^

ワイルドカードの解釈

**/*.phpのような指定をすると、zshと違いカレントディレクトリは対象外になる。
カレントディレクトリも対象とするには***.phpとする。

ls **/*.php
php/empty.php

⟩ ls ***.php
algo.php
php/empty.php