Commands
Intro
The general format of a command is:
Specifying The Executable
Commands are just regular programs (i.e., executable files). You don’t have to specify the program’s full path because the shell looks for it in PATH
, which is a environment variable (see Exporting Variables) that contains a list of directories that may contain installed binaries. So, instead of /usr/bin/ls
you can just say ls
, because PATH
contains /usr/bin
. If you ever need to add something to the PATH, put export PATH=/path/to/add:$PATH
in your ~/.bashrc
.
Options
You can change how a command behaves by appending options. Typically each option has a short form like -a
, or a long form like --all
. The short form is indicated by a single dash, and the long form by double dash. Beware that sometimes lesser-used options don’t have a short option, and short options aren’t always the first letter of the longer counterpart. You can chain short options together like so: -a -b -c
becomes -abc
.
Some options can even take values, like so:
For -u root
, you often don’t need the space between the u
and the r
(this depends on the program’s command-line argument parser), but it is recommended for readability. Note that when an option takes a value, other short options can still be chained with the value-taking option, as long as they come before (otherwise these options may be interpreted as a value).
Note that most commands come with a --help
and a -h
option, but sometimes the short option isn’t always the help message. The short option might not even be there, and vice versa. So try the long option first in case -h
does something weird.
Positional Arguments
Another type of argument is positional arguments, and they don’t need dashes. You can specify them any where in the command, as long as they are in the order that the program expects. Some commands are more finicky with their positioning though, so it may be a good habit to place all options before positional arguments, like so:
Shell Prompt (Interactive)
A prompt is displayed when an interactive shell is asking for the next command. Generally, a prompt ending in $
means “this command will be run as a regular user”, while a #
prompt means “this command will be run as root.” On modern and user-friendly shells, the prompt might also include the current working directory, username, time, and so on. In Bash, you can configure the prompt via PROMPT_COMMAND
.
Variables
Defining Variables
Note that Bash does not distinguish between text and numbers — they are all strings to Bash.
Substitution
To access the value in a variable, use $
:
Arrays
To define arrays:
Bash arrays are zero-indexed.
To append multiple elements:
To get array size:
To get a list of array indices to iterate over:
To get n
elements starting at index i
:
You can also iterate over array elements via a for loop.
Exporting Variables
Exporting a variable makes a shell variable available as a environment variable, which makes it visible to subprocesses (like commands you run in Bash). Putting it in ~/.bashrc
makes it persistent.
Basic I/O
echo: print things to the terminal
See also printf
.
read: read input from the user
cat: concatenate files
Functions
Functions are user-defined commands. There are two different but equivalent syntaxes for defining a function in Bash:
Accessing Function Arguments
The n
-th argument can be accessed through $n
. To access all arguments. To access all arguments as a string, use $*
. To access all arguments as an array, use $@
.
Return Value (Function Exit Code)
By default, a function returns 0 when it reaches the end. But a different value can be specified with return
.
You can’t directly return text from a command, but you can print text in the function, and capture its output (e.g., foo=$(func-with-output)
).
Local Variable
By default, any variable assignment is global. To make a variable local-only, prepend local
when assigning.
Redirection
Background
Each command/program/process has three “streams” to begin with. These streams help user interact with the program. Each stream is identified by a file descriptor (“fd”).
Name | fd | Purpose |
---|---|---|
stdin | 0 | Programs receive user input from stdin. |
stdout | 1 | Programs write output to stdout. |
stderr | 2 | Programs write error messages to stderr. |
Redirecting to/from file
Read file into stdin instead of typing:
Write stdout to file instead of printing on screen:
Write stderr to file instead of printing on screen:
Note: 2>/dev/null
is often used to hide error messages for a command, since /dev/null
is special file / data sink for undesired output. Everything that is redirected to /dev/null
gets discarded.
Redirecting to existing fd
Using &
, we can refer to an existing fd when redirecting (without the &
, Bash would think that you gave a filename). For instance, to redirect stderr to stdout:
Redirecting stdout and stderr simultaneously
>&
(alternatively, &>
) allows us to redirect stdin and stderr at the same time:
This bash-only operator is equivalent to >file 2>&1
.
Creating new fd
Additional fd’s can be created by simply redirecting existing streams to a new fd. The following example creates a new fd (3) to have only stdout printed on screen but both stdout and stderr is logged as well:
Boolean Expression
Logical Operators
- When two commands are connected with
&&
, the second command only executes when the first command succeeds (exit code is 0). - When two commands are connected with
||
, the second command only executes when the first command fails (exit code is non-zero). - If a bash script has
set -e
(exit upon error), then adding|| true
to commands that may fail non-fatally will prevent them from aborting the script.
Commands
- Commands can be strung together with Logical Operators and be used as conditions in If Elif Else Statements.
- Branches can be contingent on command exit codes (like
if grep postgres /etc/passwd
), with 0 being true and any other value being false. - Negation can be achieved by prepending the condition with
!
. - Grouping can be achieved through parentheses, which will create a subshell that evaluates the commands. The value of the condition will be the exit code of the subshell.
Single Bracket
Operator | Expression is true when… |
---|---|
! EXPR | EXPR is false. |
-n STRING | STRING is not empty (non-zero-sized) |
-z STRING | STRING is empty (zero-sized) |
STRING1 != STRING2 | STRING1 is not equal to STRING2 |
STRING1 = STRING2 | STRING1 is equal to STRING2 |
INTEGER1 -eq INTEGER2 | INTEGER1 is equal to INTEGER2 |
INTEGER1 -ne INTEGER2 | INTEGER1 is not equal to INTEGER2 |
INTEGER1 -gt INTEGER2 | INTEGER1 is greater than INTEGER2 |
INTEGER1 -lt INTEGER2 | INTEGER1 is less than INTEGER2 |
INTEGER1 -ge INTEGER2 | INTEGER1 is greater than or equal to INTEGER 2 |
INTEGER1 -le INTEGER2 | INTEGER1 is less than or equal to INTEGER 2 |
-e PATH | PATH exists |
-f PATH | PATH exists and is a flie |
-d PATH | PATH exists and is a directory |
-r PATH | PATH exists and is readable |
-s PATH | PATH exists and is nonempty |
-w PATH | PATH exists and is writeable |
-x PATH | PATH exists and is executable |
EXPR1 -o EXPR2 | at least one of EXPR1 or EXPR2 is true |
EXPR1 -a EXPR2 | both EXPR1 and EXPR2 are true |
Remember to quote your strings
Quote string operands when comparing using
[ ]
. Single square bracket pairs refer to thetest
command, which could misinterpret words in a single string argument as multiple arguments. The Bash-specific[[ ]]
operator does not have this requirement.
Double Bracket
The double bracket [[ ]]
is a Bash-builtin operator, and is thus not POSIX-compliant. You shouldn’t use it if you want your script to be cross-platform (a “shell script” instead of “bash script”), but otherwise it’s pretty handy in bash scripts, since you don’t need to quote strings at all (e.g., for empty or multi-word strings) and you have access to more readable boolean operators (||
, &&
, !
).
Control Flow
If/Elif/Else Statement
- See Boolean Expression.
While Loop
The loop runs until the specified condition is false.
Until Loop
The loop runs until the specified condition is true. This is the equivalent of while ! condition; do : done
.
For Loop
Number Range
Alternatively:
Iterating Over Array Elements
For more information, see Arrays.
Case (Switch) Statement
A case statement consist of an expression and different patterns, with each corresponding to one branch of code. The patterns are checked sequentially. If a certain pattern matches the expression, the flow of execution jumps to that branch. At the end of a branch, use ;;
to jump out of the case statement entirely, or use ;&
to execute the next branch without checking the pattern (since Bash 4.0).
Arithmetic
Basic Arithmetic
All bash literals are strings, but arithmetic operations are possible inside the $(())
operator. All basic arithmetic (+ - * / ** ( )
) is possible within the double parentheses operator.
Converting Bases
Any base to decimal:
Random Number
Parameter Expansion
In progress
This section is incomplete.
Testing and Debugging
echo
Nothing’s better than just echoing stuff.
Stricter Options
-e
: fails the script on non-zero exit code-u
: substitution of unset variable will fail the script-o pipefail
: errors in a pipe will fail the script
Bash Built-in Logging
- Logs are prepended with
+
. - Logs variable assignment, commands executed, and the same for subshells (prepends additional
+
with each nested subshell).
No-Op
The colon is a built-in no-op in Bash.