====== fish-shell ======
===== 概要 =====
* シェル言語の一つ
* zsh 並の補完機能がある
* さらに予測機能 (tab を押さなくても履歴機能を表示) がある
* 軽量
* ''prevd''、''nextd''、''dirh'' コマンドで、カレントディレクトリ履歴を基に移動できる。
===== インストール =====
* 最新版をインストールする\\
$ sudo add-apt-repository ppa:fish-shell/release-3
$ sudo apt update
$ sudo apt install fish
* 共用サーバ等でホームディレクトリにインストールする ($HOME/local にインストールする場合)
- cmake のバージョンが 3.2 以上でない場合、cmake をインストールする。
* [[サーバ関連/cmake のインストール]]
- 最新版のソースをダウンロードする。\\
$ wget https://github.com/fish-shell/fish-shell/releases/download/3.1.2/fish-3.1.2.tar.gz
- ソースを展開する。\\
$ tar axvf fish-3.1.2.tar.gz
- インストールする。\\
$ cd fish-3.1.2/
$ cmake -DCMAKE_INSTALL_PREFIX=$HOME/local
$ make -j 4
$ make install
* cmake の ''-DCMAKE_INSTALL_PREFIX'' オプションでインストール先を変える。
* 参考サイト: [[http://physpolyglot.hateblo.jp/entry/2015/05/08/191110 | CMakeでprefixを変える - physpolyglot]]
* ''-j 4'' は並列にコンパイルするオプションであり、この場合、4 CPU を使ってコンパイルしている。環境に応じて、変更する。
- インストールされたか確認する。\\
$ ~/local/bin/fish --version
fish, version 3.1.2
- .bashrc の末尾に追記する。\\
if [ "$SSH_TTY" != "" ]; then
BOOT_FISH=1
FISH_PATH=$HOME/local/bin/fish
if [ $BOOT_FISH -eq 1 ]; then
if [ -f $FISH_PATH ]; then
$FISH_PATH
exit
fi
fi
fi
* ''chsh'' コマンドは ''/etc/shells'' に登録されていない shell を登録することができない。
* 参考サイト: [[http://x68000.q-e-d.net/~68user/unix/pickup?%2Fetc%2Fshells | 設定ファイル:/etc/shells: UNIX/Linuxの部屋]]
* ''BOOT_FISH'' が 1 のときのみ、fish-shell 環境になる。エディタで、ここを 1 以外にすれば、bash 環境になる。
* bash が一度起動しているため、環境変数は引き継がれる。
* rsync や scp が転送できない対策として、SSH_TTY の if 文を追加した (2020/10/26)。
* 参考サイト: [[https://linux.just4fun.biz/?Linux%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A/scp%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%8F%E3%81%AA%E3%81%A3%E3%81%9F%E6%99%82%E3%81%AE%E5%AF%BE%E5%87%A6%E6%B3%95%E3%83%BBbashrc%E7%B7%A8#d1b1e007 | Linux環境設定/scpができなくなった時の対処法・bashrc編 - Linuxと過ごす]]
- 再ログインする。
===== 環境設定 =====
* プロンプトは ''fish_prompt'' 関数で定義する。
* 無銘闇人の現在のプロンプト\\
function fish_prompt
printf "%s %s%s%s%s %s%s %s\n%s" (set_color -o bryellow)"["(date +"%Y/%m/%d %H:%M:%S")"]" (set_color -o green)$USER (set_color normal)"@" (set_color -o green)(hostname) (set_color -o white)"(fish)" (set_color -o blue)"["(jobs | wc -l)"]" (set_color normal)":" (set_color -o cyan)(prompt_pwd) (set_color normal)"\$ "
end
* 表示: \\
[YYYY/MM/DD hh:mm:ss] USERNAME@HOSTNAME(fish) [JOB_NUBER]: CURRENT_DIR
$
* 表示例: \\
[2018/03/07 10:43:45] mumeiyamibito@hoge(fish) [0]: ~
$
* ''function fish_prompt'' を何度も実行して、プロンプトが決まったら、''funcsave fish_prompt'' で関数を保存するか、この関数を ''~/.config/fish/config.fish'' に保存することで、次回起動時から設定したプロンプトが適用される。後者の方法がおすすめ。
* 参考サイト:
* [[https://qiita.com/najayama/items/553329c242edc434b155 | (超入門)fishでプロンプトを変えたい人へのチュートリアル - Qiita]]
* [[http://fish.rubikitch.com/funcsave/ | funcsave:【要注意】関数定義をファイルに保存する]]
===== 文字の色 =====
* 文字の色はコマンドの直前に ''set_color'' コマンドで設定する。\\ set_color [OPTION] COLOR; COMMAND
* ''COLOR'': 色名 (''black'', ''red'', ''green'', ''yellow'', ''blue'', ''magenta'', ''cyan'', ''white'', ''brblack'', ''brred'', ''brgreen'', ''bryellow'', ''brblue'', ''brmagenta'', ''brcyan'', ''brwhite'', その他 16 進数の RGB でも指定可)
* ''OPTION''
* ''-o'': 太字モード (bold)
* ''-i'': 斜体モード (italic)
* ''-u'': 下線モード (underline)
* ''-b'': background color
* ''-r'': 反転モード
* 色名を ''normal'' にすると色やモードがリセットされる。
===== 構文 =====
==== 変数/配列 ====
* 定義
* ''set'' コマンドを使って定義する。\\ $ set VAR_NAME VALUE1 [VALUE2 VALUE3 ...]
* 意味
* ''VAR_NAME'': 変数名
* ''VALUE1'': 値
* 配列の場合、''VALUE'' をスペースで区切って指定する。
* 他のシェルと同じように変数名には ''$'' を付けずに指定する。
* 他のシェルの変数と異なりグローバル変数ではなく、ローカル変数となるため、注意。
* グローバル変数にする場合は、''-g'' オプションを付ける。\\ $ set -g VAR_NAME VALUE
* 子プロセスにも伝播させるためには ''-x'' オプションを付ける。\\ $ set -x VAR_NAME VALUE
* 呼び出し
* 他のシェルと同様に ''$VAR_NAME'' で呼び出す。\\ $ echo $VAR_NAME
* 配列の場合、1 から始まるインデックス (''INDEX'') で指定する。\\ $ echo $VAR_NAME[INDEX]
* 変数展開は以下の 2 種類
* ダブルクォーテーションで囲む: ''"$VAR_NAME"'', ''"$VAR_NAME"TEXT''
* ''{}'' で変数全体を囲む: ''{$VAR_NAME}'', ''{$VAR_NAME}TEXT''
* B シェルのように ''${VAR_NAME}'' だとエラーになるため注意する。
* 配列の要素数の取得\\ $ count $VAR_NAME
* ''count'' コマンドを使って取得する。
==== 関数 ====
* 定義
* ''function'' 〜 ''end'' で囲む。\\ function FUNC_NAME
DO_SOMETHING
end
* ''FUNC_NAME'': 関数名 (関数名の後に ''()'' や ''{}'' を付けない)
* ''DO_SOMETHING'': 関数で実行するコマンド
* 引数は ''$argv'' で受け取る。複数の引数の場合は 1 から始まるインデックスを付けて受け取る (例: ''$argv[1]'')。
* 呼び出し
* 他のシェルスクリプトと同じ\\ FUNC_NAME
==== コマンド置換 ====
* コマンドの結果を引数化する方法。
* bash などでは、以下のように実行することで中間ファイルを生成せずに目的の結果が得られる。\\ $ echo "$(cat a.txt | wc -l) + $(cat b.txt | wc -l)" | bc
- ''a.txt'' の行数を ''wc -l'' コマンドでカウントする。→ 例: ''100''
- ''b.txt'' の行数を ''wc -l'' コマンドでカウントする。→ 例: ''120''
- それぞれの行数を用いて数式を ''echo'' で表示する。→ 例: ''100 + 120''
- 数式を ''bc'' コマンドで演算する。→ 例: ''220''
* なお、''$()'' は ''``'' (バッククォーテーションで囲む) でも代用できる (ただし、ネストできない)。
* fish-shell では、単なる括弧 (''()'') でコマンド置換をする。\\ $ echo (cat a | wc -l)"+"(cat b | wc -l) | bc
==== 文字操作 ====
* マッチ\\ $ string match [-a|-i|-r|-n|-v] PATTERN STRING
* ''PATTERN'': パターン
* ''STRING'': 対象文字列
* ''-a'': 繰り返しマッチ
* ''-i'': 大文字小文字を区別しない
* ''-r'': 正規表現でパターンを指定 (デフォルトは glob (ワイルドカード))
* ''-n'': マッチした位置を返す
* ''-v'': 逆の結果を返す
* 文字の長さを取得\\ $ string length STRING
* ''STRING'': 対象文字列
* 文字の置換\\ $ string replace [-a|-i|-r|-q|] PATTERN REPLACE STRING
* ''PATTERN'': パターン
* ''REPLACE'': 置換後の文字列
* ''STRING'': 対象文字列
* ''-a'': 繰り返しマッチ
* ''-i'': 大文字小文字を区別しない
* ''-r'': 正規表現でパターンを指定 (デフォルトは glob (ワイルドカード))
* リストの文字列を結合\\ $ string join SEP LIST
* ''SEP'': 区切り文字
* ''LIST'': 文字列リスト
* 文字列をリストに分割\\ $ string split [-m|-r] SEP STRING
* ''-m'': 最大分割数
* ''-r'': 右側から分割 (デフォルトは左側から分割)
* ''SEP'': 区切り文字
* ''STRING'': 対象文字列
* 文字列のトリム (末端の文字列削除)\\ $ string trim [-l|-r|-c] STRING
* ''-l'': 左側末端の文字列を削除 (デフォルトは両端)
* ''-r'': 右側末端の文字列を削除 (デフォルトは両端)
* ''-c'': 削除する文字列を指定 (デフォルトは空白)
* ''STRING'': 対象文字列
* 位置を指定して文字列抽出\\ $ string sub [-s|-l] STRING
* ''-s'': 取得する文字列の開始位置
* ''-l'': 取得する文字列の長さ
* ''STRING'': 対象文字列
* エスケープ文字を追加\\ $ string escape [-n] STRING
* ''-n'': 出力結果をクォーテーションで囲まない
* ''STRING'': 対象文字列
* 参考サイト: [[https://blog.nijohando.jp/post/fish-shell-manipulating-string/ | fish shellで文字列操作]]
==== リダイレクトとパイプ ====
* リダイレクト
* 標準出力\\ $ COMMAND > FILE
* 標準出力 (追記)\\ $ COMMAND >> FILE
* 標準エラー\\ $ COMMAND ^ FILE
* 標準エラー (追記)\\ $ COMMAND ^^ FILE
* 標準出力と標準エラーを同時出力\\ $ COMMAND 2>&1 | cat > FILE
* パイプ (通常のシェルと同じ)\\ $ COMMAND1 | COMMAND2
==== コマンドが存在するか調べる ====
* 2 通りのコマンドがある\\ $ type -q
\\ OR\\ $ command -sq
* 結果は、''$status'' で受け取るか、''; and'' や ''; or'' (fish-shell 3.0 以降なら ''&&'' や ''||'' でも可) でつなげて受け取る。
* 成否を受け取るなら、''$status'' を用いる \\ $ echo $status
* 続けて、コマンド (コマンドが存在する場合は ''COMMAND1'' を実行し、存在しない場合は ''COMMAND2'' を実行する。)\\ $ type -q ; and ; or
* 参考サイト:
* [[https://stackoverflow.com/questions/42831558/check-if-a-program-exists-from-a-fish-script | shell - Check if a program exists from a Fish script - Stack Overflow]]
* [[https://blog.lorentzca.me/fish-shell/ | fish shell はじめました]] (終了ステータスの変数名)
===== Tips =====
==== テーマを切り替える ====
* ''fish_config'' コマンドで、ウェブインターフェースの設定画面が現れるので、そこで変更する。
* リモート環境でウェブインターフェースが使えない場合は、''set -U | grep fish_color'' で表示された内容を、リモート環境でも ''set -U'' で設定していくと同じテーマになる。
* [[https://superuser.com/questions/1243710/where-does-fish-config-save-its-settings | shell - Where does fish_config save its settings? - Super User]]
==== 環境変数を一時的に変更する ====
* 設定されている環境変数で、出力する内容が変わるプログラムがいくつか存在する。
* ''date'' コマンドなどは、使用している OS の言語で、日時のフォーマットが変わる。
* 出力する環境によっては、正常に動作しないプログラムもいくつか存在する。
* 元の言語 (たいていは英語) での出力を望む場合は直前に ''LANG=C'' を付けると良い。
* bash や zsh では、コマンド (実行プログラム) の場合、素直に ''LANG=C'' を付ける。\\ $ LANG=C date
* fish の場合は、env コマンドを使う。\\ $ env LANG=C date
* 参考サイト: [[https://www.ishiy.xyz/posts/2016-11-13-linux-fish.html | fishはじめました - iLog]]
==== find コマンドの exec が使えない ====
* 対策1: ''{}'' をシングルクォートで囲んで、最後に ''+'' を付ける。\\ 例: $ find . -name '*.sh' -exec chmod +x '{}' +
* 参考サイト: [[https://unix.stackexchange.com/questions/301225/find-exec-not-working-in-fish | find -exec not working in fish - Unix & Linux Stack Exchange]]
* 対策2: find の結果を xargs で処理する。\\ 例: $ find . -name '*.sh' | xargs chmod +x
==== if 文で優先順位のある複数の条件を扱う ====
* fish-shell では if 文の条件で ''()'' を使って優先度を変えることができない (''()'' がコマンド置換 (bash の ''$()'') に割り当てられているため)
* 対策: ''begin 〜 end'' と ''; and'' や ''; or'' を使う
* 例:
* Python での表記\\
if True and not True and False:
print("OK")
--->
if True and not(True and False):
print("OK")
---> OK
* fish-shell での表記\\
if true; and not true; and false
echo "OK"
--->
if true; and not begin true; and false; end
echo "OK"
---> OK
* 参考サイト: [[http://fish.rubikitch.com/begin/ | begin:一連のコードをまとめる【変数・リダイレクト・条件】]]
==== fish-shell を導入したリモートに rsync や scp でアクセスできない ====
* 【症状】fish-shell を導入したリモートに rsync や scp でファイルを転送しようとすると以下のエラーメッセージが表示される\\
protocol version mismatch -- is your shell clean?
(see the rsync man page for an explanation)
rsync error: protocol incompatibility (code 2) at compat.c(178) [sender=3.1.2]
* 【原因】リモートに ssh にログインした際に、標準出力 (リモートサーバの状況やグリーティングなど) されるものがある場合にエラーになるらしい (bash など他のシェルでも同様の原因の問題があるらしい)。
* 【解決方法】
* ログイン時に実行される ''config.fish'' から、標準出力 (''echo'' など) を削除、あるいはコメントアウトする。
* ログイン時に実行される ''config.fish'' の標準出力部分を ''if status --is-interactive ... end'' ブロックで囲む。
* 対話シェルの時のみ表示するようにする。
* 例: \\
if status --is-interactive
echo "test"
end
* 参考サイト: [[https://github.com/fish-shell/fish-shell/issues/3473 | Rsync and sftp doesn't work when remote host uses fish shell · Issue #3473 · fish-shell/fish-shell]]
==== コマンドラインスタック ====
* とても役立つテクニックで、buffer stack とも呼ばれるらしい…。
* 長いコマンドを打っている時に、ヘルプオプションを見たくなった時に便利。
* 長いコマンドを打っている時に、やり忘れたコマンドを実行したくなった時に便利。
* つまり、現在の入力中のコマンドを一時的に退避させる方法。
* 方法:
* 以下の関数を ''$HOME/.config/fish/config.fish'' に記述する。\\
function push-line
set cl (commandline)
commandline -f repaint
if test -n (string join $cl)
set -g fish_buffer_stack $cl
commandline ''
commandline -f repaint
function restore_line -e fish_postexec
commandline $fish_buffer_stack
functions -e restore_line
set -e fish_buffer_stack
end
end
end
function fish_user_key_bindings
bind \cs push-line
end
* 最後の ''bind ...'' でキーバインドを設定し、Ctrl + s で動作するようにしている。
* 参考サイト: [[http://turanegaku.hateblo.jp/entry/2017/06/22/015349 | fish shellでコマンドラインスタック - つらねの日記]]
==== rsync や scp で fish をインストールしたリモートサーバにファイルを転送できない (旧方法) ====
* ''.bashrc'' の先頭に記述する。\\
if [ -z "$PS1" ]; then
return
fi
* .bashrc で何らかの処理がされている場合、scp や rsync が進行しないエラーが発生するため、.bashrc がこれらのコマンドで読み込まれた際、対話モードでない場合は即座に終了するようにする。
* 参考サイト: [[https://qiita.com/montblanc18/items/b93fa4082e3bc2702a7f | .bashrc内にechoを入れておくとscpに失敗する - Qiita]]
==== ハイフンの入った文字列の置換ができない ====
* 問題: ''string replace'' でハイフンの入ったパターンを置換できない。
* 解決策: ''replace'' の後に ''--'' (ハイフン 2 つ) を入れる。
* 例:
$ string replace -- '-min' '' 'hoge-min.js'
hoge.js
* 参考サイト: [[https://github.com/fish-shell/fish-shell/issues/3416 | string command fails if input starts with a dash · Issue #3416 · fish-shell/fish-shell · GitHub]]
===== 参考サイト =====
* [[http://fish.rubikitch.com/document-ja/ | fish2.4日本語ドキュメント:すぐ使える25の便利機能と実例]]
{{tag>プログラミング Linux サーバ}}