Bash
https://www.gnu.org/software/bash/manual/bashref.html http://mywiki.wooledge.org/BashGuide
Bash is an awful language, which we nonetheless have to use because it's available everywhere. It's also quite effective for many tasks, despite its awfulness.
Configured by "$HOME/.bashrc"
. Alternatively, you can use the –rcfile flag to specify a different file.
Bash can accept commands on the command line (an interactive shell), from standard in, or from a file. When running a file, always include a shebang #!/usr/bin/env bash
.
Compatibility
–posix flag makes it behave standard.
This happens automatically if you launch bash using the sh command.
Login Shell
If it is run with no arguments, - or –login, it is assumed to be a login shell.
In this case, it will look in ~/.profile instead of ~/.bashrc.
Non-interactive Shell
If we passed Bash some arguments when we ran it, then it will be non-interactive.
It looks at $BASHENV to find a file.
Parameters
Parameter assignment is done using PARAM=something
.
Get the value of a parameter using "$PARAM"
or "${PARAM}"
. You can leave out the double quotes, but it's a bad idea.
Parameters are dynamically scoped in Bash, and always contain strings.
Parameter Expansions
There are a variety of string substitutions that you can perform when you use a parameter.
${PARAMETER:-something}
- use
something
if parameter isn't set. ${PARAMETER:=something}
- set parameter to something if it isn't already set.
${PARAMETER:?something}
- bail with an error if the parameter isn't set.
${PARAMETER:offset}
- cut the end off parameter.
${PARAMETER:offset:length}
- cut the beginning and end off parameter.
{#PARAMETER}
- length of parameter.
${PARAMETER#something}
- delete substring matching something from the start of parameter. Use double
##
to be greedy. ${PARAMETER%something
- delete substring matching something from the end of parameter. Use double
%%
to be greedy.
There are more, but htis is a reasonable start.
Special Parameters
$1
,$2
, and so on- positional parameter
$*
- all positional parameters concatenated
$@
- all positional parameters as separate words
$#
- number of positional parameters
$?
- last exit code
$-
- current shell's option flags
$$
- process id (subshell return parent's process id)
$!
- process id of last backgrounded job
$0
- script name. Shell name in interactive mode.
$_
- last positional parameter to the most recently invoked command
There are many others.
Strings
Strings quoted with 'my-string'
are literal. They also use the C backslash escape sequence.
Strings quoted with "my-string"
may be expanded. Additionally, most of the backslash escape sequences are disabled.
Strings in Bash are null-terminated C-style strings.
Brace Expansion
You can generate strings using prefix{a,b,c}postfix
.
This will create a string for each element in the list between the braces.
Command Substution
$(command)
inside a string runs the command in a subshell, and replaces that section of the string with the stdout of that command.
Process Substitution
<(command)
and >(command)
both substitute in a filename.
For <(command)
, this filename will contain the stdout of the command.
For >(command)
, writing to this filename will send stdin to the command.
This is done by creating named pipes. You can also arrange it manually with mkinfo
.
Control Flow
Chaining Commands
&& and || mean the same as in C.
& means execute the previous command in the background. The return status is 0. Never follow &
with a ;
, because it will break it.
; means sequence. Wait for the thing on the left before doing the thing on the right.
All of && || & ; create command lists.
() puts the commands list inside it into a subshell.
{} groups the commands list inside it, and returns the combined result. This is used to override operator precedence rules.
Pipes
|
connects stdout of command1 to stdin of command2.|&
does stderr and stdout. It means2>&1 |
.
These connections happen before any other redirection.
Pipes return the output of the last command in the pipe. You can turn on pipefail
to make it return the last non-zero output instead.
Each command in a pipe is executed in a subshell.
Branching
The if statement checks the return value of a predicate command.
You can use the double square brackets to test a thing, or the test command.
if [[ -z "$SOME_PARAMETER" ]]; then echo "SOME_PARAMETER was empty" else echo "SOME_PARAMETER had content" fi
The case statement pattern matches against a string.
case "$MY_PAREMTER" in a* | b*) echo "MY_PAREMTER began with 'a' or 'b'";; *) echo "MY_PAREMTER was something else";; esac
There's an alternative ;&
syntax to allow multiple cases to match. Don't use this, it will become really confusing.
The select statement prompts the user with a numbered menu. This seems a bit useless?
Looping
Bash supports:
until expression; do something; done
- do-until loop
while expression; do something; done
- do-while loop
for (( init-command ; predicate-command ; increment-command )) ; do something; done
- for loop
for token in tokens; do something-with "$token"; done
- for-each loop
Looping constructs have break
and continue
builtin statements, which behave the same as in other languages.
Functions
Functions in bash are really procedures.
While they usually look sort of Algol-like, you can also replace the curly braces {}
with a different type of compound command.
Function calls look like normal bash commands: my-function arg1 arg2
. The arguments are available as "$1"
, "$2"
and so on.
Numbers
Write (( expression ))
to use arithmetic mode. Write $(( expression ))
to substitute arithmetic results into a string.
Arithmetic in Bash is integer only, and a bit rubbish. Try to use a more general-purpose programming language for numbers work.
Debugging and Profiling
You can put the time command in front of a pipe and it will do clever things.