How to implement dynamic tab completion of options in Fish shell? - fish

I want to implement dynamic tab completion of options to a Fish command. This is easy for the -a switch, but I can't figure out how to do it for the -l switch.
Consider the following lines:
$ complete -c foo -a '(echo bar\nbaz\nbiz)' -f
$ complete -c foo -l '(echo bar\nbaz\nbiz)' -f
The behavior of my shell is then as follows:
$ foo b<tab>
bar baz biz
$ foo --<tab>
foo --\(echo\ bar\\nbaz\\nbiz\)
Instead I'd like it to suggest three options --bar, --baz and --biz. Is this possible?

edit: Now I understand better. You can do this by just making your "arguments" start with dashes. Here's an example that uses a function for clarity:
function get_foo_completions
echo --bar
echo --baz
echo --biz
set prev_arg (commandline -pco)[-1]
test "$prev_arg" = print
and echo --conditional
end
complete -c foo -a '(get_foo_completions)' -f
The --conditional argument will only be printed if the previous argument is print which illustrates that these can be dynamic.
To my knowledge this isn't yet possible. Options are declarative, and only arguments to those options may be dynamic.
If you give more details about your use case I might be able to suggest other approaches.

Related

fish shell completions keep-order

How do I use --keep-order ?
My function looks like this:
function filterfile -a file -a word
grep -i $word $file
grep -iv $word $file | sponge $file
end
and my completions look like this:
complete -k -c filterfile --require-parameter --no-files -a "(cat (commandline -opc)[2])"
complete -k -c filterfile --require-parameter
Following the documentation "Multiple complete calls with -k result in arguments of the later ones displayed first", but when I press tab for a file path nothing happens
If I understand correctly, you have a function filterfile which takes two arguments, a filename and a word to search for in the file. You would like the first argument to tab-complete as file names, and the second argument to tab-complete as words given in the file from the first argument.
You can do this by using the --condition option (short -n) to complete, documented here. Here we use the helper function __fish_is_first_arg to control when our completions run:
# Complete first argument as files.
complete -c filterfile --condition __fish_is_first_arg --force-files
# Complete remaining arguments as words in the file from the first argument.
complete --keep-order -c filterfile --condition 'not __fish_is_first_arg' --no-files -a '(cat (__fish_first_token))'
Now the first argument should tab-complete as files, and the second+ arguments should tab-complete as words found in the file named by the first argument.
(Note that __fish_is_first_arg is an ordinary fish function that ships with fish.)
To answer your original question, the --keep-order option offers completions in the order they are printed, instead of sorting. With --keep-order:
> cat words.txt
sweater
handy
scarecrow
card
geese
> filterfile words.txt <tab>
sweater handy scarecrow card geese
The completions appear in their original order. Without:
> filterfile words.txt <tab>
card geese handy scarecrow sweater
the completions are sorted alphabetically.

What are the pros/cons of 'bsub < script.sh'

Consider first script_0.sh:
# script_0.sh
foo
bar
baz
I can run this script via LSF like this, for example:
bsub -q myqueue -J myjob_0 -o path/to/log_0.out -e path/to/log_0.err -- /bin/sh ./script_0.sh
Now consider a second, very similar script:
# script_1.sh
#BSUB -q myqueue
#BSUB -J myjob_1
#BSUB -o path/to/log_1.out
#BSUB -e path/to/log_1.err
foo
bar
baz
I can also run this script, via LSF, as shown above (after changing the command-line options appropriately), but, in addition, I can do it like this:
bsub < script_1.sh
In this second form, bsub figures out the options to use by parsing the lines beginning with #BSUB .
As far as I understand, all the forms described above would entail running exactly the same commands (foo, etc.), since the two scripts are identical, if we exclude comments and blank lines.
What are the pros/cons of this second form vis-à-vis the first form? In particular, does either form make possible job scheduling strategies that are difficult or impossible to implement with the other form?

run a prolog code with swipl in a command line

I am searching for swipl the similar feature as perl -e
In particular, I want to run prolog code in this fashion:
swipl --wanted-flag "fact(a). message:-writeln('hello')." -g "message" -t halt
This is possible to do with
swipl -f file -g "message" -t halt
where the prolog clauses are written in file
I am running swipl on the server side that takes user input as prolog clauses, therefore writing a file on the server is not a good idea.
One thing you can do is to use load_files/2 with the option stream, and load from standard input, not from an argument (you can still pass the entry point as an argument, I guess):
Say in a file fromstdin.pl you have:
main :-
load_files(stdin, [stream(user_input)]),
current_prolog_flag(argv, [Goal|_]),
call(Goal),
halt.
main :- halt(1).
and with this you can do:
$ echo 'message :- format("hello~n").' | swipl -q -t main fromstdin.pl -- message
|: hello
The comments by #false to this answer and the question will tell you what this |: is, if you are wondering, but if it annoys you, just do:
$ echo 'message :- format("hello~n").' \
| swipl -q -t main fromstdin.pl -- message \
| cat
hello
instead.
This will let you read any Prolog from standard input and call an arbitrary predicate from it. Whether this is a clever thing to do, I don't know. I would also not be surprised if there is a much easier way to achieve the same.

Check if program is in path

Can sh itself check if a program exists or is in path?
I.e., not with the help of the "which" program.
I don't believe sh can directly. But perhaps something like:
which() {
save_IFS=$IFS
IFS=:
for d in $PATH; do
test -x $d/$1 && echo $d/$1
done
IFS=$save_IFS
}
and here's a nice variation that uses a subshell so that restoring IFS is not necessary:
which() (
IFS=:
for d in $PATH; do
test -x $d/$1 && echo $d/$1
done
)
Also, (in bash) if the command has been executed in the past and bash has already done the PATH search, you can see what it found with hash -t.
bash-3.2$ hash -t which
bash: hash: which: not found
bash-3.2$ which foo
bash-3.2$ hash -t which
/usr/bin/which
The utility command -v $CMD is apparently a portable option (in the sense of being part of POSIX); see also the very similar (though bash-specific) question, in particular this answer.

Escaping whitespace within nested shell/perl scripts

I'm trying to run a perl script from within a bash script (I'll change this design later on, but for now, bear with me). The bash script receives the argument that it will run. The argument to the script is as follows:
test.sh "myscript.pl -g \"Some Example\" -n 1 -p 45"
within the bash script, I simple run the argument that was passed:
#!/bin/sh
$1
However, in my perl script the -g argument only gets "Some (that's with the quotes), instead of the Some Example. Even if I quote it, it cuts off because of the whitespace.
I tried escaping the whitespace, but it doesn't work... any ideas?
To run it as posted test.sh "myscript.pl -g \"Some Example\" -n 1 -p 45" do this:
#!/bin/bash
eval "$1"
This causes the $1 argument to be parsed by the shell so the individual words will be broken up and the quotes removed.
Or if you want you could remove the quotes and run test.sh myscript.pl -g "Some Example" -n 1 -p 45 if you changed your script to:
#!/bin/bash
"$#"
The "$#" gets replaced by all the arguments $1, $2, etc., as many as were passed in on the command line.
Quoting is normally handled by the parser, which isn't seeing them when you substitute the value of $1 in your script.
You may have more luck with:
#!/bin/sh
eval "$1"
which gives:
$ sh test.sh 'perl -le "for (#ARGV) { print; }" "hello world" bye'
hello world
bye
Note that simply forcing the shell to interpret the quoting with "$1" won't work because then it tries to treat the first argument (i.e., the entire command) as the name of the command to be executed. You need the pass through eval to get proper quoting and then re-parsing of the command.
This approach is (obviously?) dangerous and fraught with security risks.
I would suggest you name the perl script in a separate word, then you can quote the parameters when referring to them, and still easily extract the script name without needing the shell to split the words, which is the fundamental problem you have.
test.sh myscript.pl "-g \"Some Example\" -n 1 -p 45"
and then
#!/bin/sh
$1 "$2"
if you really have to do this (for whatever reason) why not just do:
sh test.sh "'Some Example' -n 1 -p 45"
in:
test.sh
RUN=myscript.pl
echo `$RUN $1
(there should be backticks ` before $RUN and after $1)