Fish how to prevent recursive function calls - fish

How can I prevent recursive function calls on fish functions when overriding a default binary with a function that has the same name ?
eg.
# Override 'ls'
function ls
if [ my_special_condition ]
* Do special stuff *
else # Call regular ls
ls $argv
end
end
Obsiously the above code ends up in a resursive loop without calling the actual 'ls' binary.
Is there a way to fix this ?

Within the function, use the command command
function ls
command ls $argv
end

I understand that you want to replace the ls function while also being able to call the original. You can do that by copying the function via functions -c:
functions -c ls orig_ls # copies ls to orig_ls
function ls
if [ my_special_condition ]
* Do special stuff *
else # Call original ls
orig_ls $argv
end
end

Related

Fish shell wildcard returns different output

I'm trying to create a fish function with named argument contains a wildcard *. But the output between the function I made and the plain command are different.
Here's my function:
function ls-wildcard -a arg
ls $arg
end
and here's the result when I tried to execute it
$ ls-wildcard path/*.foo
bar1.foo
The output shows only 1 file when there should be 2 of them. But the plain ls works like a charm.
$ ls path/*.foo
bar1.foo bar2.foo
Am I missing something?
Edit:
After I tried some different expressions, the behavior of the function seems to terminate itself after the first matched. any way to fix it?
The wildcard is expanded before the function is run.
Your function uses a named argument, which is one argument.
So it is equivalent to this:
function ls-wildcard
ls $argv[1]
end
ls-wildcard path/*.foo
# runs `ls-wildcard` like
ls-wildcard path/bar1.foo path/bar2.foo
and then your function throws away the second argument.
The simplest fix is to just use $argv:
function ls-wildcard
ls $argv
end
which will forward all arguments to ls.

Set and execute a command from a function

How can I write a fish function that executes a command in a string and make it appear in the history?
function qh --description 'Use peco to query command history'
if test (count $argv) = 0
set peco_flags --layout=bottom-up
else
set peco_flags --layout=bottom-up --query "$argv"
end
history | peco $peco_flags | read cmd
if test $cmd
commandline $cmd
else
commandline ''
end
end
This does not work...
It is possible to create a key binding which sets the command line and then executes it; that command will then appear in history. Example:
function whatday
commandline "echo Today is $(date +%A)"
commandline -f execute
end
bind \eq whatday
now alt-q will set the commandline to echo Today is Sunday and execute it; it appears in history.
Beyond that there are also abbreviations which allow replacing tokens with text; but the text is just static (e.g. gco -> git checkout).
There is as yet no way for an arbitrary fish function (e.g. run as part of a shell script) to append to history, only delete and read from it.

/bin/dash: strange behavior when matching wildcard *

I want to iterate over all files in given directory. This is my script (simplified):
#!/bin/sh
for F in /tmp/ZZZ/* ; do
echo $F
done
This works as expected, when directory /tmp/ZZZ/ contains files:
$ ./myscript.sh
/tmp/ZZZ/aaa
But the problem is, when directory is empty, instead of not echoing anything, the script echos the literal *:
$ ./myscript.sh
/tmp/ZZZ/*
Why is this happening?
Is there any way change this behavior of the shell ?

How can I make a function run every time cd successfully changes to another directory within sh on FreeBSD?

I'm using sh as my shell on FreeBSD but I want to be able to have a pretty prompt like the one bash gives me on Ubuntu. There are two things that the FreeBSD implementation of sh seems to lack as far as PS1 escape characters go:
The \w works but does not expand $HOME to ~, so this is something I have already hacked up myself
I can use PS1 to update the prompt on the terminal, but as far as I can tell it is not possible to use the PS1 variable to update the title bar as well. ESC and BEL fail to set the title as one would expect if they were using bash or ksh
Here is my .shrc file
update_prompt() {
case "$PWD" in
"$HOME"*)
pretty_pwd="~${PWD#*"${HOME}"}"
;;
"/usr$HOME"*)
pretty_pwd="~${PWD#*"/usr${HOME}"}"
;;
*)
pretty_pwd="$PWD"
;;
esac
case "$TERM" in
xterm*|rxvt*)
PS1="[$USER#\\h $pretty_pwd]\\$ "
;;
*)
;;
esac
printf "\\033]0;[%s#$(hostname -s): %s]\\007" "$USER" "$pretty_pwd"
}
update_prompt
So when I fire up a terminal or log in via ssh, it gives the pretty prompt that I like. But now I need this function to run every time that cd is executed and returns an exit status of 0.
I was going to use an alias that was something like:
alias cd="cd $1 && update_prompt"
but that was before I realized that aliases do not except arguments. How might I go about doing something like this?
You can use a function instead of an alias:
cd() {
command cd "$#" && update_prompt
}
Just put it into ~/.shrc. You have to use command here to let sh know that you are referring to the actual cd builtin command instead of the function you've just defined.
Refer to the sh(1) manual page for the details on how to make sh(1) source the ~/.shrc file when it starts:
Therefore, a user should place commands that are to be executed only at login
time in the .profile file, and commands that are executed for every shell
inside the ENV file. The user can set the ENV variable to some file by placing
the following line in the file .profile in the home directory, substituting for
.shrc the filename desired:
ENV=$HOME/.shrc; export ENV
I use this trick in my cd alias manager. Here's a link to the source code of the function: https://github.com/0mp/goat/blob/v2.5.0/libgoat.sh#L31-L57
You can do it with alias+arguments if you swap the commands:
$ alias cd="echo change; cd"
$ pwd
/nas
$ cd /
change
$ pwd
/
$ cd /etc
change
$ pwd
/etc
$

I have trouble using mcc compiler in MATLAB (Error using ==> mcc The output directory does not exist)

I'm trying to build the .NET assembly file by executing this code in matlab2010b
workdir = 'C:\Users\H\Documents\Source Code\MatlabFiles';
outdir = fullfile(workdir, 'Output');
dnetdir = fullfile(workdir, 'dotnet');
%% Determine file names
mfile = fullfile(workdir, 'perform.m');
dnetdll = fullfile(dnetdir, 'dotnet.dll');
%% Create directories if needed
if (exist(outdir, 'dir') ~= 7)
mkdir(outdir);
end
if (exist(dnetdir, 'dir') ~= 7)
mkdir(dnetdir);
end
%% Build .NET Assembly
eval(['mcc -N -d ' dnetdir ' -W ''dotnet:dotnet,' ...
'dotnetclass,0.0,private'' -T link:lib ' mfile]);
I'm getting this error.
??? Error using ==> mcc
The output directory,
'C:\Users\H\Documents\Project\thesis\Source'
does not exist.
I'm pretty sure it's because of the space in the directory path "...\Source Code\...".
Because if I just use another path with no spaces it works perfectly fine.
Is there a way to make this work?
Thank you.
I think the actual problem occurs with your EVAL statement. You build a string to evaluate by concatenating strings like dnetdir and mfile, each of which will have a file path with a space in it. The resulting string you pass to EVAL will look like this:
mcc -N -d C:\Users\H\Documents\Source Code\MatlabFiles\dotnet -W ...
^--Look at that ugly space!
What you need to do is build your string so that there are apostrophes around these paths, like this:
eval(['mcc -N -d ''' dnetdir ''' -W ''dotnet:dotnet,' ...
'dotnetclass,0.0,private'' -T link:lib ''' mfile '''']);
Which will result in a string that looks like this:
mcc -N -d 'C:\Users\H\Documents\Source Code\MatlabFiles\dotnet' -W ...
And which will be evaluated properly now even with that nasty space in there.
I don't have any experience with mcc but some other functions may suffer from similar problems since most people are used to using the command mode (i.e. similar to the command prompt in DOS, Linux, Mac, ...). However, most functions are really functions such that you can use them in function mode and pass their arguments within parentheses.
You can also use mcc in function mode, as described in the help. That might look somewhat like:
mcc('-N', '-d', dnetdir, '-W', 'dotnet:dotnet,dotnetclass,0.0,private', '-T', 'link:lib', mfile);
That way you should not have to worry about escaping any character.
try changing the last line to:
eval(['mcc -N -d ''' dnetdir ''' -W ''dotnet:dotnet,' ...
'dotnetclass,0.0,private'' -T link:lib ' mfile]);
note the extra quotes around dnetdir