Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Q
quick-refs
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Michael Alan Marsh
quick-refs
Commits
8220f5e8
Commit
8220f5e8
authored
6 years ago
by
Michael Marsh
Browse files
Options
Downloads
Patches
Plain Diff
working on bash quick-ref
parent
b2b93171
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
bash.txt
+348
-0
348 additions, 0 deletions
bash.txt
with
348 additions
and
0 deletions
bash.txt
0 → 100644
+
348
−
0
View file @
8220f5e8
Bash Quick-Reference
====================
The bash man page is very long and detailed, with the result that
it can be difficult to find what you're looking for in it.
Variables
---------
There are many shell variables that can come in handy, both
interactively and in scripts. We typically write them with a dollar
sign in front, because that is how they are referenced (but not
set!).
| *Variable* | *Contents* |
| ---------- | ---------- |
| $$ | Current process (shell) PID |
| $! | PID of last subprocess started in the background |
| $? | Return value of the last completed subprocess |
| $0 | Name with which the shell/script was invoked |
| $1, $2, ... | Positional parameters to the shell/script |
| $# | Number of positional parameters to shell/script |
| $@ | Positional parameters, expanded as separate words |
| $* | Positional parameters, expanded as a single word |
| $HOME | The current user's home directory |
| $OLDPWD | The previous working directory (see "cd -") |
| $PATH | The directories searched for commands |
| $PPID | The PID of the shell/script's parent process |
| $PWD | The current working directory |
| $RANDOM | A random int in the range [0,32767] |
| $EUID | The current effective user numeric ID |
| $UID | The current user numeric ID |
| $USER | The current username |
You set a variable with
my_var=123
Referencing variables is done with a dollar sign, but there is more
than one way to do this. These are equivalent:
$HOME
${HOME}
Why would we use the longer version? Try the following:
for f in *; do echo $f0; done
Now try:
for f in *; do echo ${f}0; done
The braces give us considerable more control, and some extra features,
as we'll see.
Parameter Expansion
-------------------
See the "Parameter Expansion" section of the bash manpage for more
details and other expansion options.
| *Expansion* | *Effect* |
| ----------- | -------- |
| ${#var} | The length of *var* |
| ${var:-def} | *var*, if set, otherwise *def* |
| ${var:=def} | As above, but *var* will be set to *def* if not set |
| ${var:off} | Substring of *var*, beginning with character *off* |
| ${var:o:l} | As above, but at most *l* characters |
| ${var/p/s} | Expand *var*, replace pattern *p* with string *s* |
| | If *s* not provided, remove the pattern |
| | # and % perform prefix and suffix matches |
Quoting
-------
How a language handles single- vs double-quotes varies quite a bit.
Python treats them equivalently, while C only allows a single
character between single-quotes. Bash works a bit differently:
single-quoted strings do not have parameters expanded, while
double-quoted strings do. For example, compare:
echo '${HOME}'
echo "${HOME}"
You will most often want double-quotes, but single-quotes are very
useful when preparing input to another program. For example:
find . -name '*.txt'
which is equivalent to
find . -name \*.txt
Command Execution
-----------------
There are multiple ways to do this. Consider an executable `foo`:
| *Invocation* | *Effect* |
| ------------ | -------- |
| `foo` | foo is run as a subprocess normally |
| `foo &` | foo is run in the background, as execution continues |
| `` `foo` `` | foo is run as a subprocess, and its STDOUT is returned |
| `$(foo)` | Same as the above |
The last two are equivalent, but the $() form is preferable, because
it is clearer and can be nested.
Working with Positional Parameters
----------------------------------
Sometimes, $* or $@ are good enough:
foo $* # Pass all parameters to this other command
for a in $*; do ... # Loop over the parameters
If the parameters have different meanings, we can do the following:
a=$1
b=$2
We can make this more robust, with defaults:
a=${1:-foo}
b=${2:-bar}
We can also use `shift`, which pops the first positional parameter:
a=$1
shift
b=$1
shift
or, more compactly:
a=$1; shift
b=$1; shift
The advantage of the `$1; shift` form is that we can add more
positional parameters without having to keep count. We'll see other
uses later.
Mathematical Expressions
------------------------
Many mathematical operations can be put in $(( )). This will only
perform integer math, however. Here's an example:
total=0
for thing in $*
do
total=$(( ${total} + ${#thing} ))
done
echo ${total}
This will sum the lengths of the positional parameters, and print
the result to STDOUT.
For floating-point math, we have to use other options (such as awk).
File Descriptors
----------------
There are three automatic file descriptors (in addition to any files
your program opens):
| *File* | *Descriptor* | *Meaning* |
| ------ | ------------ | --------- |
| STDIN | 0 | Standard input, reading from the terminal |
| STDOUT | 1 | Standard output, writing to the terminal |
| STDERR | 2 | Standard error, writing to the terminal |
The normal thing for a program to do is read from STDIN and write to STDOUT.
Many programs will also write to STDERR, but if all you have is the terminal,
it's hard to tell STDOUT from STDERR. However, the shell still knows, and
lets us treat these differently. Here's an example. First, try:
grep bash /etc/*
Now, try this:
grep bash /etc/* 2>/dev/null
The second form told the shell that STDERR (2) should be redirected
to (>) the special file /dev/null. We could also do:
grep bash /etc/* 2>/dev/null >bash_in_etc
You shouldn't see any output now, but take a look at the new file
`bash_in_etc`. If unspecified, output redirection applies to STDOUT.
We can also merge STDOUT and STDERR:
grep bash /etc/* 2>&1
Here, we've specified the redirection target as `&1`, which means
"whatever file descriptor 1 points to".
To append, instead of overwriting, we can use ">>" instead of ">".
We can also redirect STDIN, by using "<":
wc -l <bash_in_etc # This counts the number of lines in the file
Here Documents
--------------
There's a special form of input redirection, using "<<":
wc <<EOFWC
this
is
a
test
EOFWC
The string "EOFWC" is arbitrary, but "EOF<command>" is fairly common. We
can also pass a single string as STDIN with "<<<":
wc <<<"this is a test"
Pipelines
---------
The Unix philosophy is that a given tool should do one thing, and
if you have to do multiple things, you should compose different
tools. Pipelines are the shell's way to do this. In short:
foo | bar | baz
takes STDOUT from foo, redirects that to the STDIN of bar, and
redirects bar's STDOUT to baz's STDIN.
You should become comfortable with this pattern, because it is one
of the keys to creating powerful scripts. We can also combine with
other things we've seen:
echo "grep produced $(grep bash /etc/* 2>&1 >/dev/null | wc -l) errors"
Control Flow
------------
Bash has an if/then/elif/else/fi construction. The minimal version is
if/then/fi, as in:
if $foo
then
echo "foo"
fi
The full form would be:
if $foo
then
echo "foo"
elif $bar
then
echo "bar"
else
echo "baz"
fi
The if statement uses command return codes, so you can put a command
in the test, or use the `test` command (usually written `[`):
grep foo /etc/hosts
have_foo=$?
grep localhost /etc/hosts
have_local=$?
if [ 0 -eq ${have_foo} ]
then
echo "We have foo"
elif [ 0 -eq ${have_local} ]
then
echo "We have localhost"
fi
See the `test` manpage for details; there are many tests you can
perform, and the manpage is fairly compact.
We can also construct loops in bash, as we've already seen briefly.
There are "for" loops and "while" loops, and they behave as you'd
expect. Both have the format:
<for or while>
do
# ...
done
A "for" statement looks like
for loop_var in <sequence>
Sequence can be something like "$*", or "$a $b $c", or $(ls /etc).
If you want to iterate over numbers, you can do something like
for loop_var in $(seq 10)
do
echo "foo${loop_var}"
done
The `while` statement takes a conditional, much like `if`. We can loop
indefinitely with it:
while true
do
# ...
if $condition
then
break
fi
done
Finally, bash has a `case` statement:
case ${switch_var} in
foo) echo "foo";;
bar|baz) echo "bar"; echo "baz";;
*) echo "default";;
esac
Let's combine this for command-line argument parsing:
a="foo"
b="bar"
c=""
while [ $# -gt 0 ]
do
case $1 in
-a) shift; a=$1; shift;;
-b) shift; b=$1; shift;;
*) c="$c $1"; shift;;
esac
done
Functions
---------
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment