Let's imagine you want to save your time writing all kubectl command: kubectl describe pods in shorter way: k d p.
So the solution is to add function to ~/.bashrc:
k() {
cmd_kubectl="command kubectl"
case ${1} in
g)
shift
kubectl_get="${cmd_kubectl} get"
case ${1} in
p)
shift
${kubectl_get} pods "$#"
;;
d)
shift
${kubectl_get} deploy "$#"
;;
n)
shift
${kubectl_get} ns "$#"
;;
i)
shift
${kubectl_get} ing "$#"
;;
j)
shift
${kubectl_get} job "$#"
;;
*)
${kubectl_get} "$#"
;;
esac
;;
d)
shift
kubectl_desc="${cmd_kubectl} describe"
case ${1} in
p)
shift
${kubectl_desc} pods "$#"
;;
d)
shift
${kubectl_desc} deploy "$#"
;;
n)
shift
${kubectl_desc} ns "$#"
;;
i)
shift
${kubectl_desc} ing "$#"
;;
j)
shift
${kubectl_desc} job "$#"
;;
*)
${kubectl_desc} "$#"
;;
esac
;;
*)
${cmd_kubectl} "$#"
;;
esac
}
But I'd like to save an effort and improve it more, so my question is:
How to enable auto-completion on this function to allow fill e.g. the name of the pod?
Usage:
k d p -> Tab -> k d p nginx (result)
I tried to think up thanks following references, but probably I'm not enough experienced Linux user/developer to compose the final solution.
How to enable kubernetes commands autocomplete
How to enable bash auto-completion for a function?
https://askubuntu.com/questions/68175/how-to-create-script-with-auto-complete
Thanks in advance!
I also wanted to make it easier to write kubectl commands, but I solved it in a slightly different way.
I will describe this method below as I think you may find it useful.
I keep all the necessary files in the ~/.bash_completion.d directory but you can modify it depending on your needs.
$ mkdir ~/.bash_completion.d
First, I enabled kubectl autocompletion as described in the Kubernetes documentation:
kubectl completion bash > ~/.bash_completion.d/kubectl
Then I downloaded complete-alias - automagical shell alias completion:
NOTE: More information on complete-alias can be found here.
$ curl https://raw.githubusercontent.com/cykerway/complete-alias/master/complete_alias > ~/.bash_completion.d/complete_alias
Next I created the kubectl_aliases file with the aliases I want to use:
$ cat ~/.bash_completion.d/kubectl_aliases
alias kgp='kubectl get pods'
complete -F _complete_alias kgp
alias kgd='kubectl get deploy'
complete -F _complete_alias kgd
alias kgn='kubectl get ns'
complete -F _complete_alias kgn
alias kgi='kubectl get ing'
complete -F _complete_alias kgi
alias kgj='kubectl get job'
complete -F _complete_alias kgj
alias kg='kubectl get'
complete -F _complete_alias kg
Finally, we can execute commands from the files in the ~/.bash_completion.d directory and check if it works as expected:
$ source ~/.bash_completion.d/kubectl
$ source ~/.bash_completion.d/complete_alias
$ source ~/.bash_completion.d/kubectl_aliases
After typing kgp and pressing Tab twice, we got a choice of Pods:
$ kgp
app-1 nginx webserver
$ kgp app-1
NAME READY STATUS RESTARTS AGE
app-1 1/1 Running 0 5m28s
Related
This question already has an answer here:
bash getopts with multiple and mandatory options
(1 answer)
Closed 1 year ago.
Answer given here doesn't here any of my questions: bash getopts with multiple and mandatory options
I am new to shell script and trying to write a shell script with getopts meanwhile I couldn't find answers to the below questions. Can somebody please help here?
This is the script I've started writing:
#!/bin/bash
while getopts c:t:x: option ; do
case $option in
c)
java -jar $file --fail-if-no-tests --include-classname "$OPTARG" --scan-class-path $file --include-tag regression
;;
t)
java -jar $file --fail-if-no-tests --include-classname '.*' --scan-class-path $file --include-tag "$OPTARG"
;;
c | t)
java -jar $file --fail-if-no-tests --include-classname "$OPTARG" --scan-class-path $file --include-tag "$OPTARG"
;;
*)
usage
esac
done
How do I pass multiple flags? For example runner.sh -c classname -t tagname I Tried using ct, c|t in the getopts but did not work.
How do I pass multiple arguments for one flag? For example runner.sh -g arg1 arg2
Is there a way if I don't provide any flag
it will run by default statement. I tried to achieve this using *
but there is one issue with this approach. In case the wrong flag is
provided it will always run default statement.
while getopts 'c:t:x:' option; do
case "$option" in
c ) classname="$OPTARG" ;;
t ) tagname="$OPTARG" ;;
x ) xvar="$OPTARG" ;;
esac
done
## now you can use the variables in your command
I need to get 4 options (each with a short and a long version) in a bash script.
Here is what I did:
OPTS=`getopt -l :author,icon,channel,message: -o :aicm: -- "$#"` ||
exit 1
eval set -- "$OPTS"
while true; do
case "$1" in
-a|--author) echo "A:'$2'"; shift;;
-i|--icon) echo "I:'$2'"; shift 2;;
-m|--message) echo "M:'$2'"; shift 2;;
-c|--channel) echo "C:'$2'"; shift 2;;
--) shift; break;;
*) echo Error; exit 1;;
esac
done
And here is what I get:
command
docker run --rm -e SLACK_TOKEN slacker notify --channel foo
output
C:'--'
Error
Of course, I would like to have this output:
C:'foo'
Your getopt command looks a little funky. You seem to be using : as some sort of delimiter, here:
-l :author,icon,channel,message:
And here:
-o :aicm:
That doesn't make any sense. The : has special meaning in the options definitions; take a look at the getopt(1) man page:
-l, --longoptions longopts
The long (multi-character) options to be recognized. More than one
option name may be specified at once, by separating the names
with commas. This option may be given more than once, the longopts
are cumulative. Each long option name in longopts may be followed
by one colon to indicate it has a required argument, and by two colons
to indicate it has an optional argument.
The same is true of short options.
So assuming that all of your options take arguments, you would write:
OPTS=`getopt -l author:,icon:,channel:,message: -o a:i:c:m: -- "$#"` ||
I am confused how to read in multiple options using getopts and how to use one directory as the source for both options. Let me be more specific...
If I were to say:
./rpsm.sh -u -g /some/directory
or
./rpsm.sh -ug /some/directory
How would I use /some/directory as the directory for both options -u and -g?
Also, how would I list the results of -u then list the results of -g (or vice versa) after running the script?
I would define a third option (-B for both?) to be used to set both -u and -g simultaneously.
while getopts "u:g:B:" opt; do
case $opt in
u) u_dir=$OPTARG ;;
g) g_dir=$OPTARG ;;
B) u_dir=$OPTARG
g_dir=$OPTARG
;;
esac
done
getopts doesn't provide for the type of shortcut you want.
I have couple of scripts which call into each other. However when I pass
Snippet from buid-and-run-node.sh
OPTIND=1 # Reset getopts in case it was changed in a previous run
while getopts "hn:c:f:s:" opt; do
case "$opt" in
h)
usage
exit 1
;;
n)
container_name=$OPTARG
;;
c)
test_command=$OPTARG
;;
s)
src=$OPTARG
;;
*)
usage
exit 1
;;
esac
done
$DIR/build-and-run.sh -n $container_name -c $test_command -s $src -f $DIR/../dockerfiles/dockerfile_node
Snippet from build-and-run.sh
OPTIND=1 # Reset getopts in case it was changed in a previous run
while getopts "hn:c:f:s:" opt; do
case "$opt" in
h)
usage
exit 1
;;
n)
container_name=$OPTARG
;;
c)
test_command=$OPTARG
;;
f)
dockerfile=$OPTARG
;;
s)
src=$OPTARG
;;
*)
usage
exit 1
;;
esac
done
I am calling it as such
build-and-run-node.sh -n test-page-helper -s ./ -c 'scripts/npm-publish.sh -r test/test-helpers.git -b patch'
with the intention that npm-publish.sh should run with the -r and -b parameters. However when I run the script I get
build-and-run.sh: illegal option -- r
which obviously means it is the build-and-run command that is consuming the -r. How do I avoid this?
You need double quotes around $test_command in buid-and-run-node.sh, otherwise that variable is being split on the white space and appears to contain arguments for buid-and-run.sh. Like this:
$DIR/build-and-run.sh -n $container_name -c "$test_command" -s $src -f $DIR/../dockerfiles/dockerfile_node
Further Info
As the comment below rightly points out, it's good practice to quote all variables in Bash, unless you know you want them off (for example, to enable shell globbing). It's also helpful, at least in cases where the variable name is part of a larger word, to use curly braces to delineate the variable name. This is to prevent later characters from being treated as part of the variable name if they're legal. So a better command call might look like:
"${DIR}/build-and-run.sh" -n "$container_name" -c "$test_command" -s "$src" -f "${DIR}/../dockerfiles/dockerfile_node"
I use following lines (hope this is best practice if not correct me please) to handle command line options:
#!/usr/bin/bash
read -r -d '' HELP <<EOF
OPTIONS:
-c Enable color output
-d Enable debug output
-v Enable verbose output
-n Only download files (mutually exclusive with -r)
-r Only remove files (mutually exclusive with -n)
-h Display this help
EOF
# DECLARE VARIABLES WITH DEFAULT VALUES
color=0
debug=0
verbose=0
download=0
remove=0
OPTIND=1 # Reset in case getopts has been used previously in the shell
invalid_options=(); # Array for invalid options
while getopts ":cdvnrh" opt; do
echo "Actual opt: $opt"
case $opt in
c)
color=1
;;
d)
debug=1
;;
v)
verbose=1
;;
n)
download=1
;;
r)
remove=1
;;
h)
echo "$HELP"
exit 1
;;
\?)
invalid_options+=($OPTARG)
;;
*)
invalid_options+=($OPTARG)
;;
esac
done
# HANDLE INVALID OPTIONS
if [ ${#invalid_options[#]} -ne 0 ]; then
echo "Invalid option(s):" >&2
for i in "${invalid_options[#]}"; do
echo $i >&2
done
echo "" >&2
echo "$HELP" >&2
exit 1
fi
# SET $1 TO FIRST MASS ARGUMENT, $2 TO SECOND MASS ARGUMENT ETC
shift $((OPTIND - 1))
# HANDLE CORRECT NUMBER OF MASS OPTIONS
if [ $# -ne 2 ]; then
echo "Correct number of mass arguments are 2"
echo "" >&2
echo "$HELP" >&2
exit 1
fi
# HANDLE MUTUALLY EXCLUSIVE OPTIONS
if [ $download -eq 1 ] && [ $remove -eq 1 ]; then
echo "Options for download and remove are mutually exclusive" >&2
echo "$HELP" >&2
exit 1
fi
echo "color: $color"
echo "debug: $debug"
echo "verbose: $verbose"
echo "download: $download"
echo "remove: $remove"
echo "\$1: $1"
echo "\$2: $2"
If I call the script way that mass arguments (those that are not switches or arguments for switches) are last arguments everything is working correctly:
$ ./getopts.sh -c -d -v -r a b
Actual opt: c
Actual opt: d
Actual opt: v
Actual opt: r
color: 1
debug: 1
verbose: 1
download: 0
remove: 1
$1: a
$2: b
The problem is when I want to call the script so the mass arguments are first (or somewhere in the middle of switches that do not use arguments)
$ ./getopts.sh a b -c -d -v -r
Correct number of mass arguments are 2
OPTIONS:
-c Enable color output
-d Enable debug output
-v Enable verbose output
-n Only download files (mutually exclusive with -r)
-r Only remove files (mutually exclusive with -d)
-h Display this help
or
$ ./getopts.sh -c a b -d -v -r
Actual opt: c
Correct number of mass arguments are 2
OPTIONS:
-c Enable color output
-d Enable debug output
-v Enable verbose output
-n Only download files (mutually exclusive with -r)
-r Only remove files (mutually exclusive with -d)
-h Display this help
I think this should be OK according (POSIX) standards, because following syntax which is basically the same is working as expected on my system:
$ cp test1/ test2/ -r
$ cp test1/ -r test2/
I have search over the Internet but only thing that was close to my problem was this one related to C.
getopts automatically breaks the while loop as soon as it detects a non-dash parameter (not including the argument given to dash parameters that take arguments). The POSIX standard is to have dashed parameters come first, and then have files. There's also none of this -- and + crap either. It's plain and simple.
However, Linux isn't Unix or POSIX compliant. It's just in the nature of the GNU utilities to be "better" than the standard Unix utilities. More features, more options, and handling things a bit differently.
On Linux, command line parameters can come after files in many GNU utilities.
For example:
$ cp -R foo bar
Work on my Unix certified Mac OS X and on Linux, However,
$ cp foo bar -R
Only works on Linux.
If you want getopts to work like a lot of Linux utilities, you need to do a wee bit of work.
First, you have to process your arguments yourself, and not depend upon $OPTIND to parse them. You also need to verify that you have an argument.
I came up with this as an example of doing what you want.
#! /bin/bash
while [[ $* ]]
do
OPTIND=1
echo $1
if [[ $1 =~ ^- ]]
then
getopts :a:b:cd parameter
case $parameter in
a) echo "a"
echo "the value is $OPTARG"
shift
;;
b) echo "b"
echo "the value is $OPTARG"
shift
;;
c) echo "c"
;;
d) echo "d"
;;
*) echo "This is an invalid argument: $parameter"
;;
esac
else
other_arguments="$other_arguments $1"
fi
shift
done
echo "$other_arguments"
I now loop as long as $* is set. (Maybe I should use $#?) I have to do a shift at the end of the loop. I also reset $OPTIND to 1 each time because I'm shifting the arguments off myself. $OPTARG is still set, but I have to do another shift to make sure everything works.
I also have to verify if a argument begins with a dash or not using a regular expression in my if statement.
Basic testing shows it works, but I can't say it's error free, but it does give you an idea how you have to handle your program.
There's still plenty of power you're getting from getopts, but it does take a bit more work.
Bash provides two methods for argument parsing.
The built-in command getopts is a newer, easy to use mechanism how to parse arguments but it is not very flexible. getopts does not allow to mix options and mass arguments.
The external command getopt is an older and more complex mechanism to parse arguments. It allows long/short options and the gnu extension allow to mix options and mass arguments.