Shell Hacks
Bash
Every Script Needs:
(Note this is opinion, not fact of best practices) `set -e` tells the script to stop when it hits errors (Bash doesn't) `set -u` tells the script to stop on unset variables `set -o pipefail` prevents the script continuing if part of the pipe fails
So:
`set -euo pipefail` in all scripts!
Iteration
Increment counter variable:
i=$((i+1)) ((i=i+1)) let "i=i+1"
Decrement counter variable:
i=$((i-1)) ((i=i-1)) let "i=i-1"
Parameter Expansion
The value of parameter will be substituted.
parameter will be expanded, and then the output will be truncated according to the pattern word from the beginning of parameter
parameter will be expanded, and then the output will be truncated according to the pattern word at the end of the parameter
This last one is particularly useful for removing file extensions.
Case syntax
case EXPRESSION in PATTERN) statement; statemnet;; PATTERN2) statement;; esac
For loop
for $i in $list do command done
If statement
if [ condition ]; then command elseif command else command fi
Conditionals
Do note there is a difference between [ ] and [ [ ] ]. [ ] is the test shell builtin. Double brackets is calling an external command.
Conditions
[ -z STRING ] : empty string [ -n STRING ] : non empty string [ STRING == STRING ] : equal [ STRING != STRING ] : not equal [ NUM -eq NUM ] : equal [ NUM -ne NUM ] : not equal [ NUM -lt NUM ] : less than [ NUM -le NUM ] : less than or equal [ NUM -gt NUM ] : greater than [ NUM -ge NUM ] : greater than or equal [ STRING =~ STRING ] : regexp && : and || : or ! : not
File conditions
[ -e FILE ] : exists [ -r FILE ] : readable [ -h FILE ] : symlink [ -d FILE ] : directory [ -w FILE ] : writable [ -s FILE ] : size is >0 bytes [ -f FILE ] : file [ -x FILE ] : executable [ FILE -nt FILE ] : newer than [ FILE -ot FILE ] : older than [ FILE -ef FILE [ : same files
Read a File Line by Line
- Default
while read -r line; do cmd1 cmd2 done < file
- Use IFS to specify field limiter
while IFS= read -r line; do cmd1 cmd2 done < file
- Note: read -p workaroung
You cannot use read -p to get user input while inside a while loop. For using data from a file to creat prompts my workaround is to read a file into an array.
declare -a LINES while read -r line; do LINES+=("$line") done < file for p in "$LINES[@]" do read -p "$p " a printf "%s %s\n" "$p $a" done
Regexp
. : matches any single character ^ : matches a term at the beginning of a paragraph or line [a-e] : matches any characters but a-e bye$ : matches bye if it occurs at the end of a line [abcde] : matches any single a, b, c, d, e
- : signifies a range of characters
() : group regular expressions {n} : matches the previous character exactly n times {n,m} : matches the previous character minimum n time, maximum m times {n,} : matches the previous character n or more times \| : matches the regexp preceeding or following ? : matches the preceeding character 1 or 0 times
* : match preceeding character 0 or more times
- : match precedding character 1 or more times
! : do not match the following regexp \ : escape special chars \b : match word boundary \n : match line break \t : match tab \w : equivalent to [a-zA-Z0-9_] \W : matches the opposite of above \d : matches digits 0-9 [:alpha:] : equivalent to [A-Za-z] [:digit:] : equivalent to [0-9] or [\d] [:alnum:] : equivalent to [A-Za-z0-9]
Write shell scripts with options:
- getopt (create options for a shell script)
This is a standalone command, so getopt will need to be installed for this to work. This provides the possibility for GNU style long options
PARAMS=`getopt -o a::bc: --long arga::,argb,argc: -n 'myscript.sh' -- "$@"` eval set -- "$PARAMS" while true ; do case "$1" in -a|--arga) case "$2" in "") ARG_A='some default value' ; shift 2 ;; *) ARG_A=$2 ; shift 2 ;; esac ;; -b|--argb) ARG_B=1 ; shift ;; -c|--argc) case "$2" in "") shift 2 ;; *) ARG_C=$2 ; shift 2 ;; esac ;; --) shift ; break ;; *) echo "Unknown option!" ; exit 1 ;; esac done