--no-scope-shadowingオプションって?
fish shellで関数定義する際,functionに--no-scope-shadowing(-S)オプションを付けると外側のスコープの変数を参照できるようになります.
色々なことができそうなオプションですけれど,具体的にどんな使い途があるか考えてみました.
使ってみる
インクリメント演算子もどき
function ++ --no-scope-shadowing
	if set -q "$argv[1]"
        set $argv[1] (math $$argv[1] + 1)
    end
end
set a 1
++ a
echo $a #=> 2
外側の変数に触れる,ということですぐに思いついたのがこちら.やんごとない理由1によりforループでなくwhileループを書かなければいけないときに役立つかもしれません.fishはインクリメントするだけでもset a (math $a + 1)って書かないといけませんからね...
ちなみにデクリメント演算子--は関数名として認められていないため再現不可です.
変数名と値を表示
function p --no-scope-shadowing
	for arg in $argv
        echo "$arg: $$arg"
    end
end
set a "Hello, fish!"
p a #=> a: Hello, fish!
変数の名前から値を調べられるのを利用.副作用は伴いません.デバッグに役立ちそう?
スワップ
function swap --no-scope-shadowing
	if begin; set -q "$argv[1]"; and set -q "$argv[2]"; end
            set -l tmp $$argv[1]
            set $argv[1] $$argv[2]
            set $argv[2] $tmp
        end
end
set a 1
set b 2
p a b #=> a: 1 b: 2
swap a b
p a b #=> a: 2 b: 1
ふたつの変数の値を交換.$argv[i]で変数の名前を,$$argv[i]で変数の値を取得しています.こういう$が連続する記法,他のシェルでもできるんですかね?
ふたつの配列を引数に取る
配列を2個受け取ってその共通項を返します.
function intersect --no-scope-shadowing
	if begin; set -q "$argv[1]"; and set -q "$argv[2]"; end
        for i in $$argv[1]
            if contains $i $$argv[2]
                echo $i
            end
        end
    end
end
set a 1 2 3 4 5
set b 3 4 5 6 7
intersect a b #=> 3 4 5
配列を展開せずに関数に渡せるのがミソです.
fishは引数がargvという一つの配列に格納されるので,--no-scope-shadowingを使わずにintersect $a $bと呼び出すような実装にするとargvのどこまでが一つ目の配列に由来するのかが分からなくなります.
おわりに
もっと色々なことができそうな気がするのですが,思いつきませんでした.何かアイデアがあればコメント等で教えていただけると幸いです.
訂正
記事タイトルが--no-scope-shadingになっていたのを,--no-scope-shadowingに直しました.
- 
fish-shellビルトイン関数縛りで seqが封印されている場合とかですね! ↩