How to add tab-completion to command with two arguments (and tab complete both)? - fish

Let's have a command command. This command has an option -o that takes 2 arguments. I'd like to add tab-completion to both of those arguments.
I've tried
complete -c command -x -s o -a "complete first arg"
I but cannot add tab-completion to the second argument.
I would like to autocomplete the command when no option is specified. this work fine:
complete -c command -a "no option completion"
but when it I hit tab after the first argument in -o option, those ^ completions are shown.
like this:
command -o "fist" <tab>
no
option
completion
if I cannot add completion for the second argument I'd like to at least remove those completions.

This command has an option -o that takes 2 arguments.
That's quite unusual. Are you sure about that?
Usually, you'll either have options that take one argument, or options that act as "flags" and change all other arguments. So you would just check their presence.
"-o" "-old-style" options also aren't as common as "--gnu-style" long options or "-s" short options, so I suggest double-checking.
complete -c command -a "no option completion"
This means to offer "no", "option" and "completion" if the command is "command".
There is no condition specified, so these are always offered.
What you want is to use the "--condition" (or "-n") option to complete. This takes script (as a string) that is executed. If it returns 0 (i.e. true), the corresponding completion (the rest of that complete invocation - the option and arguments) is offered.
Something like
# The first condition is just to see that `commandline -opc`,
# which tokenizes (-o) all tokens of the current process (-p)
# up to (but not including) the cursor (-c)
# returns just one token - which must be the command.
#
# Alternatively this condition could also be
# the inverse of the other conditions
# (offer this if nothing else would be)
complete -c command -n 'test (count (commandline -opc)) -lt 2' -a 'stuff for no argument'
# The first argument to the option we handle via "-a" to the option
complete -c command -o the-option -a 'the first argument'
# The second argument needs to be offered
# if the second-to-last token is the option.
#
# This is incomplete, because theoretically a token that
# _looks_ like the option could be offered as an argument to _another_ option.
complete -c command -n 'set -l option (commandline -opc)[-2]; test "$option" = "-o"' -a 'the second argument'

Related

how to seperate compadd values with line break (zsh)

I have a compadd function that is meant to list AWS instances tags and return that list as completion options.
_aws_instance_by_name() {
# TODO: Figure out partial completion
local instances=$(aws ec2 describe-instances --query "Reservations[*].Instances[*].{Name:Tags[?Key=='Name']|[0].Value}" --output text)
[[ ${DEBUG} ]] && echo -e "\033[34;1m[DEBUG]\033[0m ${instances}"
compadd ${instances}
}
compdef _aws_instance_by_name aws_instance_id_by_name
But when I hit tab I get all options in option delimited by \n
foo\nbar\nSuper fancy
How do I split each value into the returned suggestions?
#compdef aws_instance_id_by_name
# Wrap output in double quotes to
# preserve all whitespace.
local output="$( <command> )"
# Split on line breaks.
local -a instances=( "${(f)output}" )
# Use convenience function to get
# fancy completion features for
# free. No need to figure out
# partial completion yourself.
_wanted aws-instances 'AWS instance' \
compadd -a instances
If you paste the above into a text file whose name starts with _ and save it into a dir that’s in your $fpath, it will get picked up automatically when you run compinit. No need to call compdef.
Documentation:
Command substitution
Parameter expansion flags
Completion system:
Utility functions
Autoloaded files

How can I enable tab-completion for `#` path options to HTTPie in fish?

HTTPie accepts paths as arguments with options that include the # sign. Unfortunately, they don't seem to work with shell completions in fish. Instead, the option is treated as an opaque string.
To stick with the file upload example from the HTTPie documentation with a file at ~/files/data.xml, I would expect to be able to tab complete the file name when typing:
http -f POST pie.dev/post name='John Smith' cv#~/files/da<TAB>
However, no completion is offered.
I have installed the completions for fish from the HTTPie project and they work for short and long arguments. This file does not specify how to complete the # arguments though.
In addition, I looked into specifying my own completions but I am not able to find a way of getting to work file completions with the arbitrary prefix.
How could I implement a completion for these path arguments for HTTPie?
Currently, the fish completions for HTTPie do not have completion for file path arguments with #. There is a more general GitHub Issue open about this.
If this is something you'd like to work on, either for yourself or for the project, you might be able draw some inspiration for the fish implementation from an HTTPie plugin for zsh+ohmyzsh that achieves your desired behaviour.
I managed to get the tab completion of the path arguments working with some caveats.
This adds the completion:
complete -c http --condition "__is_httpie_path_argument" -a "(__complete_httpie_path_argument (commandline -t))"
With the following functions:
function __is_httpie_path_argument
set -l arg (commandline -t)
__match_httpie_path_argument --quiet -- $arg
end
function __match_httpie_path_argument
string match --entire --regex '^([^#:=]*)(#|=#|:=#)(.*)$' $argv
end
function __complete_httpie_path_argument
__complete_httpie_path_argument_helper (__match_httpie_path_argument -- $argv[1])
end
function __complete_httpie_path_argument_helper
set -l arg $argv[1]
set -l field $argv[2]
set -l operator $argv[3]
set -l path $argv[4]
string collect $field$operator(__fish_complete_path $path)
end
The caveat is that this does not expand any variables nor the tilde ~. It essentially only works for plain paths — relative or absolute.

Passing optional parameters to rundeck script

I have a python script that I would like to run using rundeck that is invoked as follows:
createInstance.py [-n <name>] <env> <version>
Where name is optional and env and version are required.
e.g. if I want to call the script with a name I would call:
createInstance.py -n test staging 1.2.3.4
If I want to default the name, I would call:
createInstance.py staging 1.2.3.4
The problem i have is that I dont know how to specify the script arguments string in rundeck. I have a job, with 3 options, one for env, version and name and if I define the arguments string as:
-n ${option.name} ${option.env} ${option.version}
Whenever the name is unset, rundeck calls:
createInstance.py -n staging 1.2.3.4
Instead I would like it to omit the -n. Is there any way of doing this? Right now my only option is to change the script to be more forgiving in how it handles the -n, and to always ensure its at the end, e.g.:
createInstance.py staging 1.2.3.4 -n
createInstance.py staging 1.2.3.4 -n test
I would like to avoid making this change though, as I want to be able to use the scripts standalone as well.
Rather than use a command step, try an inline script step. Your inline script can count the number of arguments and if they are set. Then with that logic you can choose how to set the creteInstance.py args.
As #Alex-SF suggests, I've also used an inline script for this, along with a Key Value Data log filter. The script is:
#!/bin/bash
# Parse optional parameters
# https://stackoverflow.com/questions/41233996/passing-optional-parameters-to-rundeck-script
# Arguments to this script should be in the format "flag" "value", eg "-p" ${option.name}
# If value is not missing then return will be "flag value", otherwise blank
echo -n "RUNDECK:DATA:"
while (( "$#" )); do
flag="$1"
value="$2"
if [[ -z "$value" ]] || [[ $value =~ ^\- ]]; then
# no value for this parameter (empty or picking up the next flag)
echo -n ""
shift
else
# value provided for this parameter
echo -n "$flag $value "
shift
shift
fi
done
And the key value data filter uses the pattern ^RUNDECK:DATA:(.*)$ and the name data args. Then I use ${data.args*} as the input for the real command.
It's all rather messy, and I can't find any open issue requesting this as a feature (yet).
Use an inline script and use conditional variable expansion feature from bash.
createInstance.py ${RD_OPTION_NAME:+-n $RD_OPTION_NAME} $RD_OPTION_ENV $RD_OPTION_VERSION
This will omit the first option altogether if it is empty ("").

svnlook changed -t "$rev" "$repos" not getting executed

As in title I am calling from my post-commit hook script written in perl which has command
$msg = `$svnlook changed -t "$rev" "$repos"`;
which should execute and than I should send $msg to my service. But when I run
if ( length($msg) == 0 )
{
print STDERR "msg length is 0";
exit(1);
}
I get this error message on console, so why is this svnlook command not being executed?
I am using windows 7 and VisualSVN server.
On other note, I had other theory to run this command in hook itself like
#echo off
set repos=%1
set rev=%2
set changes=svnlook changed %repos% -r %rev%
C:\Perl64\bin\perl C:\repositories\myproject\hooks\myhook.pl %1 %2 changes
but I don't know how to pass this changes parameter, so if this could work, it could answer as well.
How to pass parameter from batch to perl script?
running svnlook changed help display the list of valid options to svnlook changed and their expected format:
$ svnlook help changed
changed: usage: svnlook changed REPOS_PATH
Print the paths that were changed.
Valid options:
-r [--revision] ARG : specify revision number ARG
-t [--transaction] ARG : specify transaction name ARG
--copy-info : show details for copies
Normally you would specify either a transaction number with -t or a revision number with -r. You appear to be passing a revision number with -t which will lead to unexpected results: either no results or results that are unrelated to the revision you wish to example.
I believe the correct usage in your case would be:
my $msg = `$svnlook changed -r "$rev" "$repos"`;
The above command is going to give you one long string that is delimited by newlines. You can get this is a more manageable array format by using the same command in list context:
my #changes = `$svnlook changed -r "$rev" "$repos"`;
additionally these lines will all have trailing newlines, you can eliminate them using the chomp() built-in:
my #changes;
chomp(#changes = `$svnlook changed -r "$rev" "$repos"`);
Alternatively, you could look at SVN::SVNLook which a Perl wrapper around the svnlook command.

Python Piping 'type' question

>> type countlines.py | python countlines.py
Can someone explain the what "type" does? Does it just type the components of the file out? Can't find any documentation
On windows, 'type' is the equivalent of the 'cat' command on linux, and yes - it just prints out the content of the file.
From the bash man page:
type [-aftpP] name [name ...]
With no options, indicate how each name would be interpreted if
used as a command name. If the -t option is used, type prints a
string which is one of alias, keyword, function, builtin, or
file if name is an alias, shell reserved word, function,
builtin, or disk file, respectively. If the name is not found,
then nothing is printed, and an exit status of false is
returned. If the -p option is used, type either returns the
name of the disk file that would be executed if name were speci‐
fied as a command name, or nothing if ``type -t name'' would not
return file. The -P option forces a PATH search for each name,
even if ``type -t name'' would not return file. If a command is
hashed, -p and -P print the hashed value, not necessarily the
file that appears first in PATH. If the -a option is used, type
prints all of the places that contain an executable named name.
This includes aliases and functions, if and only if the -p
option is not also used. The table of hashed commands is not
consulted when using -a. The -f option suppresses shell func‐
tion lookup, as with the command builtin. type returns true if
all of the arguments are found, false if any are not found.