Fish Shell Series
- Good, So So, Bad about Fish Shell
- DRY(Do not Repeat Yourself) in fish
When We Need Functions
Moving files, compling source codes, checking out git repositories ā¦ To get jobs done, We are frequently use command line tool.
Even if just change your working diretory ā behind the scene ā your shell is working for you to get informative prompt thesedays, isnāt it?
To do some work easier or faster, we could recall by searching your shell history or you could make a function to do the tasks.
What is Function in Fish Shell
A piece of codes
A function is a block of code that is reusable and performs certain operations.1 And Iād like to remind you an UNIX principle, KISS!
KISS: Keep it simple and stupid
We need to best of ourselves but keep our programme simple and stupid at the same time, to make a function:
- a maintainable size of block
- in a more readable form
WARNING: It is different from the code block.
A code block inherit parent local variables:
fish> set -l local_test "outside"
fish> begin # begin a code block
echo $local_test
end
"outside"
However, function donāt inherit them by default.
fish> set -l local_test "outside"
fish> function echo_local_test # begin a code block
echo $local_test
end
fish> echo_local_test
fish>
But you can read with -S option. Iād like to talk about little later.
A part of your session
Function is so common in progamming langauges, however, I found that it has some different aspects along with the similarity.
Even though We could call them in a shell, A function is not an individual programme.
A function is a part of your shell session, if we are using
exit
in a function, it will actually shut down your current session.We cannot control it as a process as this is not a child process, if we are using
sleep
in a function, it will actually sleep your current session.
fish> function sleep_10sec; sleep 10; end
fish> sleep_10sec &
... paused ...
^C
fish: Job 1, 'echo_e &' has ended
fish> # note I canceled by pressing Ctrl-C on x86_64 linux
So if you want to function act as a programme, you need to put in a separate file. and execute in another (child) fish shell. But how to make a shell script is beyond this article, Iāll post about it maybe another article.
function as aliasing
I use emacs daily, and sometimes I use emacs in terminal as well and below function will make an alias for shorter name of the programme.
# ~/.config/fish/functions/em.fish
function em --description 'alias em emacs -nw $argv'
emacs -nw $argv
end
function mc --description 'alias mc emacsclient $argv'
emacsclient -tc $argv
end
Another famous example of aliasing might be:
function rm
rm -i $argv
end
and fish shell provide wrapper function alias
as well.
alias rmi="rm -i"
This is actually care about more like below:
alias rmi="rm -i"
# This is equivalent to entering the following function:
function rmi --wraps rm --description 'alias rmi=rm -i'
rm -i $argv
end
āwraps rm provides autocompletion which is same as rm. and makes a description on behalf of you.
how about abbr?
works differently, but also helpful as abbr replace your typing words and you can still edit your typing as well. Please read about this for more information.
Function requirement
Variable
To use variables in function is common, even though we donāt need at all sometimes.
a variable could be set by set
. One big diffent thing from bash is that
set
doesnāt require any =
sign. So you could possibly make some typo
<<test target>>
fish> set -l local_var = "my_example_value"
fish> set -S local_var
$local_var: set in local scope, unexported, with 2 elements
$local_var[1]: |=|
$local_var[2]: |my_example_value|
so local_var
above becomes an array. Which makes me hard to debug sometimes. because
fish will trust you. As there was no syntax error.
Return value
A function is not a programme, but at the same time return value is quite similar to a programme as return value will always be an unsigned char(integer) value.
fish> function test_return; return -1; end
fish> test_return
fish> echo $status
255
fish> functions -e test_return
There are some way to save its return value, however using echo and using command subsitution is a common way because it is common for unix tiny programmes, to communicate each other via pipe, fifo.
fish> set today (date "+%Y-%m-%d")
fish> echo $today
2022-04-20
Arguments
As you can see in rmi alias in the prior example, $argv is a special variable which takes all the arguments you passed.
fish> function print_first_arg; echo $argv[1]; end
fish> print_first_arg "hi" "fish" "shell"
"hi"
Note: Index is string from 12
Input / Output
I/O is communication. Nn the communication between Your shell and function or
function to another function, we will use shell subsitution like the prior example.
we can use pipe |
. And this is how KISS works, too.
fish> echo "test.org" | sed 's/\.org$/\.md/'
test.md
And those kind of I/O action quite important and used very often in shell programming.
Function named āfunctionā
Now it is time to make a function. fish has straight function. meanwhile go lang has func, kotlin has fun, rust has fn ā¦
-d option
this is an optional but quite helpful when you decide to make a function. Do you remember āKISSā? To clarify what you excatly want to get from the function is the main key. some lines of description will do the basic guide line.
function elem -d 'determine first argument occurs in the list(rest of arguments)'
# do the job
end
-S option
fish shell has distinguishable concepts in variable scope. To to access local variables in the parent, We need to turn on the -S option.
-S or āno-scope-shadowing
allows the function to access the variables of calling functions. Normally, any variables inside the function that have the same name as variables from the calling function are āshadowedā ā¦
So it is possible to some function check parent local variable, too.
function elem -d 'determine first argument occurs in the list(rest of arguments)' \
-S
if set -q given_list # note not 'set -q $given_list'
# use given_list variable to test
else
# or reading from the rest of arguments
end
It depends on your function design, but in this case, we can say that local variable is safer to use here.
Note: bash doesnāt have local variable outside a function.
bash> local a="Apple"
bash: local: can only be used in a function
bash>
Refactor our example āelemā function
Now, letās focus on elem function which was imcomplete.
elem function will determine first argument occurs in the list(rest of arguments). so we need some loop to go through our argument.
if the first argument occurs again elem function will return
true
or returnfalse
(more specifically, return āreturn value of trueā, return āreturn value of falseā)and If possible when we found first arument in the rest of aruments, it would be nicer we can quit earlier.
our first elem function
function elem -d 'determine first argument occurs in the list(rest of arguments)'
set found 0
for arg in $argv[2..-1]
if test $found -eq 0 && test $arg = $argv[1]
set found 1
break
end
end
if test $found -eq 1
true
else
false
end
end
put into functions directory for permanent access
In bash, A function could be save in your ~/.bashrc
or equivalent file.
On the other hand, fish provides more organized way to save it.
In other words, you could put a function into your ~/.config/fish/functions/
!
Genrally the filename is same as your function name. In the prior case, you could
name it ~/.config/fish/functions/elem.fish
. and now you can use them in another
fish shell section or next login as well!
still need to source
But if you start from put a function in the functions directory, you still need to
source
sometimes as your change may be not yet reloaded.
fish> source ~/.config/fish/functions/elem.fish
Wait.. why I made āelmā function?
I used this function when I wanto add some path to my executable $PATH
,
And actually, This whole article is starting from this my post on dev.to.
so.. I have created a recursion in my blog posts.
Footnotes
Introduction to Function in Shell Scripting: https://www.educba.com/function-in-shell-scripting/ā©ļø
Index starting from 1 not 0 : https://jeongoon.github.io/posts/2022-04-16-about-fish-shell.html#index-starting-from-1-not-0ā©ļø