Bash Scripting: Difference between revisions
mNo edit summary |
No edit summary |
||
| Line 94: | Line 94: | ||
|Get the URL from <a href="..."> | |Get the URL from <a href="..."> | ||
|<code>''echo '<a href="asdf">'<nowiki/>''<nowiki> | grep -Eo 'href="[^\"]+"' | sed 's/href="\(.*\)"/\1/'</nowiki></code> | |<code>''echo '<a href="asdf">'<nowiki/>''<nowiki> | grep -Eo 'href="[^\"]+"' | sed 's/href="\(.*\)"/\1/'</nowiki></code> | ||
|} | |- | ||
|Replace substring in Bash | |||
| | |||
{| class="wikitable" | |||
|- | |||
|Use <code>${variable/search_term/replace_term}</code>. Eg:{{highlight|lang=bash|code= | |||
Path="/etc/resolv.conf" | |||
# This returns /etc/hostname | |||
echo ${Path/resolv.conf/hostname<nowiki>}</nowiki> | |||
}} | |||
|}|} | |||
==Useful scripting patterns== | ==Useful scripting patterns== | ||
Revision as of 20:01, 15 June 2022
Useful functions
Here are some common functions and commands I use in my scripts.
| Function | Code | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Use this to ensure the script runs relative to its location.
This is useful if your script uses relative paths. |
#!/bin/bash
cd "$(dirname "$0")"
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Find the maximum of two numbers. You can implement min by flipping the operands. | max (){
echo $(($1>$2 ? $1 : $2))
}
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Similar to the die command in PHP which prints out a message before stopping execution.
Note that this will not work inside a subshell as it will just quit the subshell rather than the script. To get around this, you may need to kill the parent PID instead. |
function die {
echo $@
exit
}
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Indents text piped to the function. | # Supports up to 10 tabs
function Indent (){
Tabs="\t\t\t\t\t\t\t\t\t\t"
sed "s/^/${Tabs:0:$((2*$1))}/g"
}
echo "test" | Indent 1
test
echo "test" | Indent 2
test
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Extracting the file name and file extension from a path | Path="/etc/resolv.conf"
# Get path parts
Directory=$(dirname $Path) # /etc
Filename=$(basename $Path) # resolv.conf
# Get filename parts
Name="${Filename%.*}" # resolv
Ext="${Filename##*.}" # conf
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Skip the first N lines from an output | ## -n +N outputs starting on line N.
## Eg. To skip the first 2 lines, we start on line 3:
# tail -n+3
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Get the current Unix timestamp | $ date +"%s"
1596137957
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Unix timestamp to human readable time.
You can use either |
$ echo 1355514777 | awk '{ print strftime("%c", $1) }'
Fri 14 Dec 2012 12:52:57 PM MST
$ date +"%c" -d @15996137957
Thu 30 Jul 2020 01:39:17 PM MDT
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Get a file's last modified time in Unix timestamp | stat -c %Y filename
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Convert a multi-line list into a comma delimited string | cat list | paste -s -d, -
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Get all IPv4 addresses on this host | ip a | grep -ohE 'inet [0-9.]{7,15}'
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Get the URL from <a href="..."> | echo '<a href="asdf">' | grep -Eo 'href="[^\"]+"' | sed 's/href="\(.*\)"/\1/'
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Replace substring in Bash |
Useful scripting patternsGet a specific columnYou can use either $ echo one two three four five | awk '{print $3}'
three
$ echo one,two,three,four,five | awk -F, '{print $3}'
three
Or cut by a specific delimiter and select a specific column. This method cannot handle columns separated by more than one space. If you have neat columns, you'll need to pipe through $ echo one two three four five | cut -d ' ' -f3
three
$ echo "one two three four five" | tr -s ' ' | cut -d ' ' -f3
three
$ echo one,two,three,four,five | cut -d ',' -f3
three
read which can take in a list of variables. Variable assignment by piping to read works in zsh but will only work if used within a subshell in bash. Keep this behavior in mind if you choose to use this method of separating out words in a string. For example:## Basic variable assignment with read
$ echo one two three four five | while read _ second _; do echo $second ; done
two
## zsh variable assignment works without a subshell/loop
% echo one two three four five | read _ second _
% echo $second
two
_ (like in golang) and ignored.## Remember, to get only the 3rd column, we need to read the 4th column out.
% echo one two three four five | read _ _ third; echo $third
three four five
% echo one two three four five | read _ _ third _ ; echo $third
three
## Delimiters specified with IFS
% echo one,two,three,four,five | IFS=',' read _ _ third _ ; echo $third
three
awk or tr+cut are actual commands that spawns a sub-process whereas read is a bash built-in function. As a result, it's always faster to use read to parse out data, especially if in a loop. If you are looping and using awk or cut, perhaps consider replacing it with the bash read equivalent. For example:
## Super slow! Invokes awk 2 times per line
$ w | tail -n +3 | while read i ; do
Username=$(echo "$i" | awk '{print $1}')
Source=$(echo "$i" | awk '{print $3}')
echo $Username $Source
done
## Instead do something like this!
$ w | tail -n +3 | while read Username _ Source _ ; do
echo $Username $Source
done
Preserve header while sorting without using a temporary fileYou can sort an output on a specific column while preserving the header and without needing a temporary file by using a shim function that reads and prints the first line before passing the rest of the output down the pipe. Define this function: # print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
IFS= read -r header
printf '%s\n' "$header"
"$@"
}
Then use this shim body function with the desired sort function. For example: $ top -b -n 1 | head -n 15 | tail -n +7 \
| body sort -k 5 -rn
Credit to https://unix.stackexchange.com/questions/11856/sort-but-keep-header-line-at-the-top for this neat trick. Testing things in Bash
Use the test operators listed below with $ test -e /etc/hosts && echo "File exists"
File exists
## Note that the white space around [ and ] and also the operator are required.
$ [ -e /etc/hosts ] && echo "File exists"
File exists
$ if [ -e /etc/hosts ] ; then echo "File exists" ; fi
File exists
IntegersInteger tests, with Eg. $ test 2 -eq 2 && echo equal
Integer tests can also be done inside double parentheses. Eg. $ ((2==2)) && echo equal
Strings
Command SubstitutionCommand substitution allows output from a command to be substituted in place of the command itself in a script. Commands can be enclosed within backticks ## Eg.
echo "Today is " $(date)
echo "Today is " `date`
Do not confuse Nested SubstitutionIf you are trying to do something similar to: Something=`basename `ls /home/*.txt``
You will get an error since the expansion is ambiguous. Instead, use the Something=$(basename $(ls /home/*.txt))
Arithmetic OperationsDo integer arithmetic operations on variables within double brackets. For example: Value=1
ValuePlusOne=$((Value + 1))
ValueTimes12=$((Value * 12))
The operators that are supported are: + - / * % Bitwise operators also work: ^ (XOR) | (OR) & (AND) << (Shift left) >> (Shift right) Control BlocksSwitchread -p "What do you want to do?: " Option
case "$Option" in
"K")
install_new_kernel
die "Complete!"
;;
"H")
usage
;;
*)
die "Stopping installation."
;;
esac
See Also
|