notes bashguide
Building CLI
Motivation
Make an interface for a script with following:
has help
has a back button
has a save button
tells the user what to do
Resources
Example
Autocompletion in BASH
Resource
link to a blog goes deep link to a stack overflow thread tutorial referred in the stack overflow\
Example
This example comes from the abovemnetioned stack overflow thread.
Following code was added to ~/.bash_functions file.
This code uses the compgen and complete builtins (which manual pages I have not seen, but it looks amazing to make any CLI application useful.
Future
figure how to supply names of the commands to the function through a file
use it in an application
Select command
Motivation
be able to make menus
Resources
link to the gnu bash manual link to the bashguide from wiki wooledge
Syntax
select name [in words …]; do commands; done
Examples
Case command
Motivation
get rid of if else nested statements
enable single choice/ multiple choice for interaction with a user
Resources
man bash page from gnu wiki wooledge bash guide
Syntax
the normal case statement is useful in case I want to have a single choice of multiple possibilities
Single match
In case you want to find the first match of the case, execute the code and exit use ;; at the end of the pattern
Multiple matches
In case you do not want to exit after the first match, but keep searchiing and executing use the ;;? at the end of the pattern
Fallthrough
In case you want to follow the match with automatic execution of the next line, use the ;& at the end.
This is good for some more complex cases which are over my head, but good to know.
Examples
All the examples mentioned here are in the ~./bash_functions file.
"Single choice"
"Multiple choice"
"Multiple choice with hierarchy and crosstalk"
Chapter 4 Parameters
assignment syntax:
varname=vardata
there must NOT be any space between the varname-identifier and vardata-value\
Special character $ followed by identifier is required for the expansion of the parameter into its value.\
In order to supply the value of the variable in one piece into any relevant command, the $identifier needs to be quoted like this: "$identifier".\
4.1 Special Parameters and Variables
The difference between parameters and variables:
variables are parameters denoted by names
special parameters are $ followed by number or special characters such as *, @, #, ?, $, !, - ie parameters which are not variables\
0
name or the path of the script, not always reliable
1
Positional parameters passed to the current script or function
*
expands to all the words of all the positional parameters
@
expands to all the words of all the positional parameters, double quoted it expands to a lis of the all as individual words
#
expands to a number of all positional parameters
?
expands to the exit code of the most recently completed foreground command
$
Expands to the PID of the current shell
!
Expands tho the pid of the command most recently executed in the bacground
_
Expands to the last argument fo the last command that was executed
Some inbuilt Variables
BASH_VERSION PWD current working directory\
4.2 Variable types
Array:
declare -a <variable>or<variable>()array of stringsAssociative array:
declare -A <variable>associative arrayInteger:
declare -r <variable>Read Only:
declare -r <variable>the variable cannot be modified or unsetExport:
declare -x <variable>the variable is exported by default
4.3 Parameter Expansion
This is super useful, but I feel I am a little bit too tired to study this.
Cutting the parameter based on lenght
${parameter:offset:lenght}
when array
In case you supply array than the numbers offset and lenght are the indices numbers
fruits=(apple apricot banana coconut or51nge)
echo ${fruits[@]:1:2}
apricot banana\
when single string value
In case there is single string supplied, the offset and lenght are the indexes along the strind.
echo ${fruits[1]:1:2}
pr\
Counting the lenght of parameter or array
${#parameter}\
echo ${#fruits[1]} to get the lenght of the second string
echo ${#fruits[@]} to get the lenght of the array\
Patterns
some expansion works on the patterns:
[ab] to detect a or b
? for one or more
for zero or more
[0-9] for digits
Deleting matching pattern in the beggining
normal
${parameter#pattern}\
Deletes the shortest matching pattern.
echo ${fruits[@]#ap}
Again if applied to the array, acts on all the items.\
greedy
${parameter##pattern}
Deletes the longest matching pattern.
echo ${fruits[@]##ap}\
Deleting matching pattern in the end
normal
${parameter%pattern}
echo ${fruits[@]%na}
Again if applied to the array, acts on all the items.\
greedy
${parameter%%pattern}
Deletes the longest matching pattern.
echo ${fruits[@]%%na}\
Substituting/deleting unanchored pattern
Acting only on the first match
${parameter/pattern/string}
when the '/string' part is missing it means deletion of the pattern
Acting on all matches
${parameter//pattern/string}
when the '/string' part is missing it means deletion of the pattern
Substitution/deletion of matching patterns anchored at beginning or end
${parameter/#pattern/string} useful for adding a common prefix ${parameter/#/string}
${parameter/%pattern/string} useful for adding a common suffix ${parameter/%/string}\
\newpage
Chapter 5 Patterns
Intro
There a three different systems for a pattern matching in the shell.\
Chapter 5 Patterns
Intro
There a three different systems for a pattern matching in the shell.\
Globs expansion
Extended globs expansions
Since the version 3.0 bash supports regular expressions
5.1 Glob Patterns
Used for finding files in the shell\
*asterix matches any string including the null string?questionmark matches any SINGLE character[...]matching any of the enclosed characters
Globs are implicitly anchored at both ends. For example a* does not match cat The * and ? characters cannot be used to substitute for / in the file system.
However it can be used to match / in patterns.
var="test/of/split"
echo ${var/st?} gives teof/split
5.2 Extended Globs
The extended Globs are more powerful, more similar to regular patterns.
It is disabled by default, needs to be activated by shopt command shopt -s extglob\
For occurence of given patterns (list):\
? (list): matches zero or one occurences
(list): matches zero or more occurences
(list): mathces one or more occurences
@ (list): matches one of the given patterns
! (list): matches anything but the given patterns
List inside the parentheses is a list of globs or extended globs separated by vertical pipe |.
$ ls
names.txt tokyo.jpg california.bmp
$ echo !(*jpg|*bmp)
names.txt\
5.3 Regular Expressions
The main difference between the regular expressions and the extended globs is that the regular expression can be only used to compare strings.
The bash version >3.0 supports the =~ operator and the [[ keyword.
Since 3.2 bash the regular expression should not be quoted.
It is a good idea to put the regular expression into a variable and then use the =~ operator.
5.4 Brace {...} Expansion
{...} ExpansionWorks similarly to globs. Happens before the glob expansion.\
echo th{e,a}ngivesthen than\echo th{1..3}engivesth1en th2en th3enecho {1..2}{a..c}gives1a 1b 1c 2a 2b 2cecho {/home/*,/root}/.*profilegives/home/vladimir/.profile /root/.*profilesome more intricacies:
brace expansion enables both going forward and backward
brace expansion ignosres the local variables like LANG and LC_COLLATE and always goes with the ASCII codes
ASCII codes:
COntain only the upper and lower characters, brackets, underscore, apostrophe and the roof symbol.
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ ] ^ _ a b c d e f g h i j k l m n o p q r s t u v w x y z`\
Therefore you can do something like this:
var=2my[var
${var/[A-z]/XYZ_} gives you $ 2XYZ_y[var
using the all matching \${var//[A-z]/XYZ_} gives you $ 2XYZ_XYZ_XYZ_XYZ_XYZ_XYZ_
newpage\
Chapter 7 Arrays
Intro
It is not useful to use single string to keep multiple values by using separator ( like
item1, item2, item3oritem1 item2 item3).Arrays are good to keep the different values well separated
The different items can be expanded using their indices numbers (starting with [0]), or used all [@] etc.
The numbers of items can be counted ${#var[@]}
Example
This does not work in general case:
files=$(ls *.jpg); cp $files /backups
The problem is that the $files is read by the cp command which expects parameters separated by spaces.
In case there are some empty spaces in the name of the files, the cp takes only the first part of the filename to copy which can be dangerous.
It is better to put the $files into "$files" to avoid the splitting.
However it is not enough for some of the other specific characters such as newline etc.\
This works:
mkdir test
cd test && mkdir backup
touch a.md b.md c.md
files=(*.md); cp "${files[@]}" backup/\
7.1 Creating arrays and sparse arrays
using the (..) syntax
names=("vlada" "jane" "agapornis beautiful bird")
It is not necessary to quote the items of the array, however it uses a space as a separator so in case you have something with a space you definitely need to qoute it!\
Definig indices directly
The indices of the array can be directly specified:
names2=([0]="vlada" [2]="jane" [1]="agapornis beautiful bird")
The indeces do not have to be uninterrupted line of integers, there can be holes in the indeces, this is called sparse arrays.
names[25]="little baby"\
To get the output of the array:\
echo $namesis wrong, gives you just the first itemecho ${names[@]}is correct gives you all the items of the array
The same applies in case you want to assign this variable values to another array:\
var=("${names[@]}")is the proper way
WRONG\
var=$namespopulates thevarvariable with the first item of thenamesarrayvar=$names[@]populates thevarvariable with the first item of thenamesarray with[@]appended.var=${names[@]}populates the [0] index with all the items from names separated by spacevar=(${names[@]})creates array, but as long as there is a space somewhere, splits the item and adds more items to it.
How do I append another value to the array?\
By using the var+=() notation.\
names+="little baby boy"
This has some unexpected behaviour, adds the "little baby boy" into second place, without keeping the separator between the first original item and the inserted string. Now I understand it, it just gets the first item and concatenates the new string to the first one, however I do not know how to add to the end of the array, need to read further obviously.
My way
The [..] brackets of indices automatically works with math inside.
In order to add the new item at the end requires the addition of 1 to the total numbers of indices.\
names[${#names[@]}+1]="new names item"
Looks rather complicated to be the only right solution, but who knows.
7.2.1 Expanding arrays pg. 58
To understand the structure of the array use the declare -p <variable> command.
declare -p names see that here I do not need to prepend it with the dollar sign as in echo.
$ declare -a names=([0]="vlada" [1]="jane" [2]="agapornis the beautiful bird")\
In case you want to have the print more nice and organized use the printf syntax.
$ printf '%s\n' "${names[@]}" where %s means the array item is a string followed by a newline. But it can be followed by pipe or whatever string character you choose.
The thing to note here is that printf loops through the array on its own by default, in case we are using some other command we need to make the for loop ourselves.\
Using the array in a for loop:
for file in ${myfiles[@]}; do cp "$file" backup/; done\
Would be great if in here you could also use a range of the indices
[0-2]or something similar. However puttingecho ${names[0-2]}does not work so I leave this open for today.*
Make a file containing the names of the files with correct delimiter (the null byte \0)
Do not really understand how to use it but I guess I can keep it in for now.
myfiles=(*.md)
printf '%s\0' "${myfiles[@]}" > myfiles.txt\
Difference between the [@] and [*] array expansion:
[*] gives you all the items of the array, mungles them together as a single string which is practical if you want to just simply list them and use some delimiter.
(IFS=,; echo "Today these guests are most welcome: ${names[*]}") \
all is enclosed in parentheses () to make sure that the IFS is changed just in the subshell
the IFS takes just the one charcter as the delimiter, in case you want to separate by something more, you need to do it different way using printf or a for loop.
Getting the number of items in the array echo ${#names[@]}
7.2.2 Expanding Indices
Very often it is not useful to expand the items, but to expand the indices. For example when you have an array of first names and second names.\
first=(vlada jane agapornis)
second=(vinarsky edelweiss "beautiful bird")
echo "${first[1]} ${second[1]}"\
Indices expansion integrated in a for loop (1)
for name in "${!first[@]}"; do echo "${first[name]} ${second[name]}" ; done \
Indices expansion in a for loop (2)
In case the array is not sparse, we can do some intersting math inside the for loop in case we want to take for example every second item of the collection or something in the same vein.
a=(a b c q w x y z)
for ((i=0; i<${#a[@]}; i+=2)); do
echo "${a[i]} and ${a[i+1]}"
done\
7.3 Associative arrays
There can be also key-values pairs in the arrays.
The array needs to be defined using declare -A <name of the associated array> As far as I understand it it is impossible to use the indices in the square brackets to get the values.
You can iterate over them using the ${!<associative array>[@]} approach but the numbers of the indices cannot be used reliably to keep the same order.
Of course for the associative arrays other variables can be used to expand the value of the pair.\
Example:
declare -A assocArray
assocArray=(["vladimir"]="muz" ["jane"]="zena")
echo "when directly called by key 'vladimir': "${assocArray["vladimir"]}""
echo "when '\$USER' variable is used as a key: "${assocArray["$USER"]}""
echo "game over"\
Last updated