よくある質問(FAQ)

bash(または他のシェル)のあの機能に対応するものは何ですか?

bashユーザーのためのfish を参照してください。

環境変数を設定または消去するにはどうすればよいですか?

set コマンドを使用します。

set -x key value # typically set -gx key value
set -e key

fish 3.1 以降、他のシェルと同様に key=value some command という構文を使用して、1つのコマンドに対してのみ環境変数を設定できるようになりました。以下の2行はまったく同じように動作します。他のシェルとは異なり、fish ではどちらの場合も value が出力されます。:

key=value echo $key
begin; set -lx key value; echo $key; end

「エクスポート(exported)」は スコープ ではなく、変数の追加の状態であることに注意してください。変数は「グローバルかつエクスポート」、「ローカルかつエクスポート」、あるいは「ユニバーサルかつエクスポート」になり得ます。通常、エクスポートする変数はグローバルにするのが理にかなっています。

変数が定義されているかどうかを確認するにはどうすればよいですか?

set -q var を使用します。例えば、 if set -q var; echo variable defined; end のように記述します。複数の変数をチェックする場合は、次のように andor と組み合わせることができます。:

if set -q var1; or set -q var2
    echo either variable defined
end

定義された変数であっても、要素を持たない場合( set var のように設定した場合)や、空の要素のみを持つ場合( set var "" のように設定した場合)は、空である可能性があることに注意してください。これらへの対処法については、以下を読み進めてください。

変数が空でないことを確認するにはどうすればよいですか?

string length -q -- $var を使用します。例えば、 if string length -q -- $var; echo not empty; end のように記述します。 string length に複数の変数のリストを渡すと、「いずれかが真(OR条件)」として解釈されることに注意してください。:

if string length -q -- $var1 $var2 $var3
    echo at least one of these variables is not empty
end

または、 test -n "$var" を使用します。ただし、 変数をダブルクォートで囲む必要がある ことに注意してください。例えば、 if test -n "$var"; echo not empty; end のように記述します。 test コマンド自体も、 and(-a)や or(-o)といった論理演算機能を提供しています。:

if test -n "$var1" -o -n "$var2" -o -n "$var3"
    echo at least one of these variables is not empty
end

変数に*要素が一つもない*かどうかを知りたい場合は、 set -q var[1] を使用してください。

なぜ set -Ux (エクスポートされたユニバーサル変数)が機能しないように見えるのですか?

同名のグローバル変数が既に存在しているためです。

EDITORTZ などの環境変数は、 set -Ux を使用してユニバーサル変数として設定できます。しかし、 fish が起動する前に(ログインスクリプトやシステム管理者などによって)既に設定されている環境変数がある場合、それらはグローバル変数として fish にインポートされます。 変数のスコープ は「内側から外側へ」の順に検索されます。つまり、最初にローカル変数がチェックされ、次にグローバル変数、最後にユニバーサル変数の順で確認されます。

これは、グローバルな値がユニバーサルな値よりも優先されることを意味します。

この問題を回避するには、fishが継承する元の設定を変更することを検討してください。それが不可能な場合は、 設定ファイル (通常は ~/.config/fish/config.fish )に以下の記述を追加してください。

set -gx EDITOR vim

ログインするたびにコマンドを実行するにはどうすればよいですか? .bashrc や .profile に相当するものは何ですか?

~/.config/fish/config.fish [1] ファイルを編集してください。存在しない場合は作成してください。(先頭にドットがあることに注意してください)

.bashrc や .profile とは異なり、このファイルは非対話型シェルやログインシェルであっても常に読み込まれます。

対話型シェルでのみ何かを実行したい場合は、次のように status is-interactive をチェックしてください。

if status is-interactive
    # use the coolbeans theme
    fish_config theme choose coolbeans
end

プロンプトを設定するにはどうすればよいですか?

プロンプトは fish_prompt 関数の出力です。この関数を ~/.config/fish/functions/fish_prompt.fish に配置します。例えば、シンプルなプロンプトは以下のようになります。

function fish_prompt
    set_color $fish_color_cwd
    echo -n (prompt_pwd)
    set_color --reset
    echo -n ' > '
end

また、Web設定ツールの fish_config を使用して、サンプルプロンプトのギャラリーからプレビューして選択することもできます。

あるいは、コマンドラインから fish_config を使用することもできます。

> fish_config prompt show
# displays all the prompts fish ships with
> fish_config prompt choose disco
# loads the disco prompt in the current shell
> fish_config prompt save
# makes the change permanent

既存のプロンプトを修正したい場合は、 funcedfuncsave を次のように使用します。

>_ funced fish_prompt
# This opens up your editor (set in $EDITOR).
# Modify the function,
# save the file and repeat to your liking.
# Once you are happy with it:
>_ funcsave fish_prompt

これは fish_right_promptfish_mode_prompt にも当てはまります。

なぜプロンプトに [I] と表示されるのですか?

それは fish_mode_prompt です。 fish_vi_key_bindings を使用してviモードを有効にしている場合、デフォルトで表示されます。

意図的にviモードを有効にしていないのであれば、それを実行するサードパーティ製のテーマやプラグインをインストールしている可能性があります。

この表示を変更または無効にしたい場合は、例えば funced などを使って fish_mode_prompt 関数を修正してください。

構文ハイライトの色をカスタマイズするにはどうすればよいですか?

fish_config で起動するWeb設定ツールを使用するか、 fish_color 系の環境変数 を変更するか、次のように fish_config theme サブコマンドを使用してください。

> fish_config theme show
# to demonstrate all the colorschemes
> fish_config theme choose coolbeans
# to load the "coolbeans" theme

挨拶メッセージ(グリーティング)を変更するにはどうすればよいですか?

変数 fish_greeting の値を変更するか、 fish_greeting 関数を作成してください。例えば、挨拶を表示しないようにするには次のようにします。

set -U fish_greeting

ユニバーサル変数を使用したくない場合は、次のようにします。

set -g fish_greeting

これを config.fish に記述します。

履歴からコマンドを実行するにはどうすればよいですか?

コマンドの一部を入力してから、 up () 、 down () 矢印キーを押して履歴の一致項目を移動するか、 ctrl-r を押して検索可能なページャで履歴を開きます。このページャ内では、 ctrl-rctrl-s を押すことで、それぞれ古い履歴または新しい履歴に移動できます。

その他のデフォルトのキーバインドには、 ctrl-p (上) および ctrl-n (下) が含まれます。詳細は 検索可能なコマンド履歴 を参照してください。

なぜ履歴置換("!$" など)が機能しないのですか?

履歴置換(history substitution)は、対話的な行編集が普及する前に発明された扱いにくいインターフェースだからです。fish はこのような擬似構文を追加する代わりに、優れた履歴検索機能や呼び出し(リコール)機能を採用しています。使いこなすには習慣を少し変える必要があります。古い行や単語を修正したい場合は、まずそれを呼び出してから編集してください。

特殊なケースとして、履歴置換の多くは sudo !! として使われます。その場合は alt-s を押してください。直前のコマンドラインの先頭に sudo を付加して呼び出します(現在のコマンドラインに何か入力されている場合は、 sudo プレフィックスの有無を切り替えます)。

一般的に、fish の履歴呼び出しは次のように動作します。

  • 他のシェルと同様に、上矢印キー( up )は、最後に実行された行から順に行全体を呼び出します。したがって、 !! と入力する代わりに、上矢印キーを押します。

  • 探している履歴のずっと後ろにある場合は、その行の一部を入力してから「上」キーを1回以上押してください。これにより、入力したテキストを含む行だけが絞り込まれて呼び出されるため、目的の行に素早く到達できます。これは !vi!?bar.c などの代わりとなる機能です。より多くの文脈(前後関係)を確認したい場合は、ctrl-r を押して履歴をページャで開くこともできます。

  • alt-up は、最後に実行された行の最後の引数から順に、個々の引数を呼び出します。これは "!$" の代わりに使用できます。

fish での行編集の詳細については、 ドキュメント を参照してください。

とは言え、 abbreviations (省略形) を使って履歴置換を実装することも可能です。以下は !! のみの例です。

function last_history_item; echo $history[1]; end
abbr -a !! --position anywhere --function last_history_item

これを実行すると、コマンドラインのどこであっても !! が直前の履歴エントリに置き換わります。設定を維持するには、 config.fish に記述してください。

サブコマンドを実行するにはどうすればよいですか?バッククォートが機能しません!

fish はサブコマンドに括弧を使用します。例:

for i in (ls)
    echo $i
end

また、引用符内であっても、おなじみの $() 構文をサポートしています。バッククォートは、POSIX シェルですら非推奨とされているため、サポートされていません。入れ子(ネスト)にしにくく、シングルクォート ('') との区別が難しいためです。

コマンド(pkg-configなど)の出力が、1つの長い文字列になってしまいます。

他のシェルとは異なり、fish はコマンド置換の結果を改行でのみ分割し、スペースやタブ、$IFS に含まれる文字では分割しません。

つまり、次のように実行した場合、

count (printf '%s ' a b c)

"a b c " が一塊として扱われるため、 1 が出力されます。しかし、次のようにすると、

count (printf '%s\n' a b c)

count に "a", "b", "c" が個別の引数として渡されるため、 3 が出力されます。

圧倒的多数のケースにおいて、スペースでの分割は望ましくないため、これは改善と言えます。結局のところ、スペースを含むファイル名で問題が起きるのはこれが原因だからです。

しかし、特に pkg-config や関連ツールでは、スペースでの分割が必要な場合があります。

そのような場合は、次のように string split -n " " を使用してください。

g++ example_01.cpp (pkg-config --cflags --libs gtk+-2.0 | string split -n " ")

-n を指定しているのは、POSIXシェルの挙動と同様に空の要素を削除するためです。

コマンドの終了ステータスを取得するにはどうすればよいですか?

$status 変数を使用します。これは、他のシェルで使用される $? 変数に代わるものです。

somecommand
if test $status -eq 7
    echo "That's my lucky number!"
end

成功か失敗かにのみ関心がある場合は、コマンドを直接 if 文の条件として実行できます。

if somecommand
    echo "Command succeeded"
else
    echo "Command failed"
end

あるいは、最初のコマンドが成功または失敗した場合に別のコマンドを一つ実行したいだけなら、 andor を使用します。

somecommand
or someothercommand

詳細については、 条件文 および testif のドキュメントを参照してください。

コマンドが "No matches for wildcard" と表示されますが、bashでは動作します。

手短に言えば、ワイルドカードを 引用符で囲むエスケープ してください。

scp user@ip:/dir/"string-*"

fish は引用符で囲まれていない * を見つけると、 ワイルドカード展開 を実行します。つまり、与えられた文字列に一致するファイル名を探そうとします。

ワイルドカードがいかなるファイルにも一致しない場合、fish はコマンドを実行せずにエラーを表示します。

> echo *this*does*not*exist
fish: No matches for wildcard '*this*does*not*exist'. See `help expand`.
echo *this*does*not*exist
     ^

bash も同様にファイルをマッチさせようとしますが、一致するものが見つからない場合、ワイルドカード文字列をそのまま(リテラルとして)渡します。

これは、次のようなコマンドが

scp user@ip:/dir/string-*

または

apt install postgres-*

動作しているように見えることを意味します。なぜなら、ほとんどの場合において文字列は(ファイル名に)一致せず、そのまま渡された string-* を受け取り側のプログラムが解釈するからです。

しかし同時に、一致するファイルが(作成されたり、別の作業ディレクトリで実行されたりして)見つかった瞬間に、これらのコマンドが意図通りに動作しなくなることも意味します。これに対処するため、bash では次のようなワークアラウンドが必要になります。

for f in ./*.mpg; do
      # We need to test if the file really exists because
      # the wildcard might have failed to match.
      test -f "$f" || continue
      mympgviewer "$f"
done

(出典: http://mywiki.wooledge.org/BashFAQ/004)

これらの理由から、fish はこのような挙動はせず、展開を意図していないアスタリスクについては引用符で囲むかエスケープすることを要求します。

これは bash の "failglob" オプションに近い挙動です。

fish をログインシェルにしていると、SSH/SCP/rsync が正常に接続できないのはなぜですか?

この問題は、 "Received message too long" 、 "open terminal failed: not a terminal" 、 "Bad packet length" 、またはデバッグログ内の ssh_exchange_identification メッセージに奇妙な出力が含まれる "Connection refused" といったメッセージとして現れることがあります。

これは通常、fish が ユーザー設定ファイル (~/.config/fish/config.fish) を、対話型・ログイン・非対話型・非ログインシェルの区別なく*常に*読み込むために発生します。

これにより物事が単純化されますが、一方で config.fish が出力を生成する場合、ssh/scp/rsync が接続時に起動する非対話型シェルであってもその出力が行われてしまうことを意味します。

config.fish 内で出力を生成するものはすべて、 status is-interactive (あるいは好みで status is-login )でガード(保護)する必要があります。

if status is-interactive
  ...
end

例えば、ガードなしで config.fish 内で tmux を起動する場合も同様です。その場合、 sessions should be nested with care, unset $TMUX to force といったメッセージが表示される原因になります。

表示が崩れたり、変な挙動(階段現象、ゴースト文字の発生、カーソル位置のズレなど)が起きます。

ターミナルにおいては、カーソル移動を正しく制御するために、その中で実行されているアプリケーションとターミナル自身の間で、文字幅の認識が一致している必要があります。

fish では構文ハイライトやオートサジェスチョン(自動提案)といった機能がカーソル移動によって実現されているため、他のシェルよりもこの一致が重要になります。

時として、この文字幅の認識が食い違うことがあります。これには多くの原因と解決策があります。

  • その文字がシステムにとって新しすぎて認識できない可能性があります。この場合は、その文字の使用を控えてください。

  • fish またはターミナルがその文字を知らないか、扱いを誤っている可能性があります。この場合、fish またはターミナルを修正するか、修正済みのバージョンにアップデートする必要があります。

  • その文字が「曖昧な(ambiguous)」幅を持っており、fish が幅 X と考えているのに対し、ターミナルが Y と考えているケースです。この場合は、ターミナルの設定を変更するか、 $fish_ambiguous_width に正しい値を設定してください。

  • The character is an emoji and your system only supports Unicode 8. In this case set $fish_emoji_width to 1.

これはまた、以下のようないくつかの事項がサポート不可能であることを意味します。

  • 等幅フォント(monospace)以外のフォント。fish はターミナルのフォント描画に干渉できないため、特定の文字がどのような幅を持つかを知る*方法がありません*。

  • 複数の「曖昧な幅」を持つ文字に対して、個別に異なる幅を割り当てること。fish は、あなたが各文字にどの幅を割り当てたかを知る方法がありません。

特定のターミナルで fish が動作しません

そのターミナルが fish の要件 をすべて満たしていない可能性があります。そのターミナルおよび fish の各イシュートラッカー(issue tracker)に報告してください。

fish のアンインストール

fish をアンインストールする場合は、まず fish が使用中のシェルとして設定されていないことを確認してください。不明な場合は chsh -s /bin/bash を実行してください。

パッケージマネージャでインストールした場合は、そのパッケージマネージャのアンインストール機能を使用してください。自身でビルドし、 /usr/local にインストールしたと仮定すると、次のように行います。

rm -Rf /usr/local/etc/fish /usr/local/share/fish ~/.config/fish
rm /usr/local/share/man/man1/fish*.1
cd /usr/local/bin
rm -f fish fish_indent