Bash

Online GNU Docs https://www.gnu.org/software/bash/manual/html_node/index.html#SEC_Contents

Redirections

Online GNU Docs: Redirections: https://www.gnu.org/software/bash/manual/html_node/Redirections.html

each process starts with three streams:

  • stdin, fd=0

  • stdout, fd=1

  • stderr, fd=2

# redirect std err, to prevent it from printing on the console or other files
sudo apt update 2>/dev/null

# redirect stdout and std err to separate files
ls -la 1>ls_stdout 2>ls_stderr

# invalid option to demonstrate the error stream
ls -! 1>ls_stdout 2>ls_stderr

# redirect stdout to a file, and stderr to stdout
ls -la &> ls_out

# which is equivalent to
ls -la 1>ls_out 2>&1

# append-redirect stdout to a file, and stderr to stdout
ls -la &>> ls_out

# Redirect stdout to stdout (FD 1)
echo "hello there" 1>&1

# Redirect stdout to stderr (FD 2)
echo "hello there" 1>&2

# Redirect stderr to stdout
echo "hello there" 2>&1

Bash Pipelines

# cmd1 | cmd2
groups | sed 's/\s\+/,/g'
  • the | to pipe stdout to stdin of next cmd

  • the |& to redirect stderr to stdout before piping to stdin

  • the |& is equivalent to to cmd1 2&>1 | cmd2

Another idea is to use xargs

build and execute command lines from standard input

# generate a sequence of 5 numbers, and build arguments for the echo command
seq 0 5 | xargs echo

# generate a compact listing of all users on the System
cut -d: -f1 < /etc/passwd | sort | xargs echo

# pipline over SSH for a series of hosts

for k in thinkpad uraspi znode;
do ssh $k "mkdir /bits/zkeys && mv /bits/vpnkeys /bits/zkeys/pki"; done

Bash Command Lists

# execute A, then execute B if A was successful
echo A && echo B

# execute B only if A fails
echo A || echo B

# execute A, then Execute B
echo A; echo B

# execute A, B and C in parallel
echo A & echo B & echo C &

Bash special parameters (Special Variables)

GNU Man Pages: Special Parameters

https://www.gnu.org/software/bash/manual/bash.html#Special-Parameters

$$ own process ID

echo $$
MYPID=$$
echo $MYPID

$! the pid of the last background-ed process

# run in the background
tail -f /var/log/dmesg &

bgtail_pid=$! # better store the PID before it flies
echo $bgtail_pid

# and then later use it, probably by sending a signal
kill -s SIGQUIT $bgtail_pid

$# the count of positional args starting of a function from 1

a_test_func () {
    echo $#
}

a_test_func hello world "a quoted hello world, making a total of 3 args"

$* and $@ expansion of positional arguments, starting from 1

There are subtle differences between the two:

  • "$*" is equivalent to "$1 $2 ..."

  • "$@" is equivalent to "$1" "$2" ...

a_test_func () {
    echo got $# args: "$*"
    echo alt syntax: "$@"
}

a_test_func hello world "a quoted hello world, making a total of 3 args"

$? the exit status of the most recent foreground pipeline

last_status=$? # better store it right away
echo $last_status # then do something with it

$- Bash flags

echo $- # expands to the current bash flags by the set built in command

$1 Position Parameters

echo $0 # expands to the name of the shell, or the shell script invoked

echo $1 # the first positional argument

echo $2 # the second positional argument

Bash Builtins

The declare built-int

declare --help

declare -F # print declared bash function names

declare -f # print declared bash function names and definitions

The set build-in, and Bash flags

set --help

echo $- # expands to the current bash flags by the set built in command

set -e # exit upon non-zero exit code

echo $- # the e flag should now be visible among the default flags

set +e # undo the e flag

generate error on expanding unbound variables
set -u

Bash Loops and Foreach

# explicit positional parameters
for k in bananas apples oranges; do echo $k; done

# expanding a list of tokens
xlang="C Python Bash Javascript"
for k in $xlang; do echo "I love $k"; done

# expanding a function that products a lis of tokens
for k in $(echo C Python Bash Javascript); do echo "I love $k"; done

# looping over arrays
xt=( a b c d )
for item in ${xt[@]}; do echo $item; done

path_tokens=( $( echo "$PATH" | tr ":" " " ) )
for item in ${path_tokens[@]}; do echo $item; done

Bash Tokenization and Splitting

The Internal Field Separator Variable $IFS can be used to determine the splitting character

DEFAULT_IFS=$IFS

langs="python;javascript;ruby"
IFS=";"; for k in $langs; do echo "$k"; done


# Then change it to split the $PATH string
IFS=":"; for p in $PATH; do echo "$p"; done

IFS=$DEFAULT_IFS

# or simply, to go back to the shell default

unset IFS

Another way is to use tr ":" "\n" to replace characters in a string or a stream

langs="python;javascript;ruby"
for k in $(echo $langs | tr ";" " "); do echo "$k"; done


# Then change it to split the $PATH string
for p in $(echo $PATH | tr ":" " "); do echo "$p"; done