Bash/Linux
October 21, 2022

Bash cheatsheet

Cheatsheets

$(< file) # Тоже что $(cat file)
|& # На ввод следующей команде попадет как stdout так и stderr
expr ? expr : expr 
условие ? выраж1 : выраж2 # Условный оператор в форме тернарного. Если условие истинно, тогда выполниться выраж1 в противном случае выраж2

Variables

NAME="John"
echo $NAME
echo "$NAME"
echo "${NAME}"

String quotes

NAME="John"
echo "Hi $NAME"  #=> Hi John
echo 'Hi $NAME'  #=> Hi $NAME

Shell execution

echo "I'm in $(pwd)"
echo "I'm in `pwd`"

Conditional execution

git commit && git push # AND
git commit || echo "Commit failed" # OR

Functions

get_name() {
  echo "John"
}
echo "You are $(get_name)"

Conditionals

if [[ -z "$string" ]]; then
  echo "String is empty"
elif [[ -n "$string" ]]; then
  echo "String is not empty"
fi

-a file # True if file exists.
-b file # True if file exists and is a block special file.
-c file # True if file exists and is a character special file.
-d file # True if file exists and is a directory.
-e file # True if file exists.
-f file # True if file exists and is a regular file.
-g file # True if file exists and its set-group-id bit is set.
-h file # True if file exists and is a symbolic link.
-k file # True if file exists and its "sticky" bit is set.
-p file # True if file exists and is a named pipe (FIFO).
-r file # True if file exists and is readable.
-s file # True if file exists and has a size greater than zero.
-t fd # True if file descriptor fd is open and refers to a terminal.
-u file # True if file exists and its set-user-id bit is set.
-w file # True if file exists and is writable.
-x file # True if file exists and is executable.
-G file # True if file exists and is owned by the effective group id.
-L file # True if file exists and is a symbolic link.
-N file # True if file exists and has been modified since it was last read.
-O file # True if file exists and is owned by the effective user id.
-S file # True if file exists and is a socket.
file1 -ef file2 # True if file1 and file2 refer to the same device and inode numbers.
file1 -nt file2 # True if file1 is newer (according to modification date) than file2, or if file1 exists and file2 does not.
file1 -ot file2 # True if file1 is older than file2, or if file2 exists and file1 does not.
-o optname # True if the shell option optname is enabled. The list of options appears in the description of the -o option to the set builtin (see The Set Builtin).
-v varname # True if the shell variable varname is set (has been assigned a value).
-R varname # True if the shell variable varname is set and is a name reference.
-z string # True if the length of string is zero.
-n string # True if the length of string is non-zero.
string1 == string2
string1 = string2 # True if the strings are equal. ‘=’ should be used with the test command for POSIX conformance.
string1 != string2 # True if the strings are not equal.
string1 < string2 # True if string1 sorts before string2 lexicographically.
string1 > string2 # True if string1 sorts after string2 lexicographically.
arg1 OP arg2 # OP is one of ‘-eq’, ‘-ne’, ‘-lt’, ‘-le’, ‘-gt’, or ‘-ge’. These arithmetic binary operators return true if arg1 is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to arg2, respectively. Arg1 and arg2 may be positive or negative integers. When used with the [[ command, Arg1 and Arg2 are evaluated as arithmetic expressions

Strict mode

set -euo pipefail
IFS=#39;\n\t'

Brace expansion

echo {A,B}.js
{A,B}       # Same as A B
{A,B}.js    # Same as A.js B.js
{1..5}      # Same as 1 2 3 4 5
{0..10..2}  # Same as 0 2 4 6 8 10

Parameter expansions

Basics

name="John"
echo ${name}
echo ${name/J/j}    #=> "john" (substitution)
echo ${name:0:2}    #=> "Jo" (slicing)
echo ${name::2}     #=> "Jo" (slicing)
echo ${name::-1}    #=> "Joh" (slicing)
echo ${name:(-1)}   #=> "n" (slicing from right)
echo ${name:(-2):1} #=> "h" (slicing from right)
echo ${food:-Cake}  #=> $food or "Cake"
length=2
echo ${name:0:length}  #=> "Jo"

STR="/path/to/foo.cpp"
echo ${STR%.cpp}    # /path/to/foo
echo ${STR%.cpp}.o  # /path/to/foo.o

echo ${STR##*.}     # cpp (extension)
echo ${STR##*/}     # foo.cpp (basepath)

echo ${STR#*/}      # path/to/foo.cpp
echo ${STR##*/}     # foo.cpp

echo ${STR/foo/bar} # /path/to/bar.cpp

STR="Hello world"
echo ${STR:6:5}   # "world"
echo ${STR:-5:5}  # "world"
SRC="/path/to/foo.cpp"
BASE=${SRC##*/}   #=> "foo.cpp" (basepath)
DIR=${SRC%$BASE}  #=> "/path/to/" (dirpath)

Substitution

${FOO%suffix}   # Remove suffix
${FOO#prefix}   # Remove prefix
${FOO%%suffix}  # Remove long suffix
${FOO##prefix}  # Remove long prefix
${FOO/from/to}  # Replace first match
${FOO//from/to} # Replace all
${FOO/%from/to} # Replace suffix
${FOO/#from/to} # Replace prefix

Comments

# Single line comment
: '
This is a
multi line
comment
'

Substrings

${FOO:0:3}  # Substring (position, length)
${FOO:-3:3} # Substring from the right

Length

${#FOO} Length of $FOO

Manipulation

STR="HELLO WORLD!"
echo ${STR,}   #=> "hELLO WORLD!" (lowercase 1st letter)
echo ${STR,,}  #=> "hello world!" (all lowercase)

STR="hello world!"
echo ${STR^}   #=> "Hello world!" (uppercase 1st letter)
echo ${STR^^}  #=> "HELLO WORLD!" (all uppercase)

Default values

${FOO:-val} # $FOO, or val if not set
${FOO:=val} # Set $FOO to val if not set
${FOO:+val} # val if $FOO is set
${FOO:?message} # Show error message and exit if $FOO is not set
# The : is optional (eg, ${FOO=word} works)

Loops

# Basic for loop
for i in /etc/rc.*; do
  echo $i
done
# C-like for loop
for ((i = 0 ; i < 100 ; i++)); do
  echo $i
done
# Ranges
for i in {1..5}; do
    echo "Welcome $i"
done
# With step size
for i in {5..50..5}; do
    echo "Welcome $i"
done
# Reading lines
< file.txt | while read line; do
  echo $line
done
# Forever
while true; do
  ···
done

Functions

# Defining functions
myfunc() {
    echo "hello $1"
}

# Same as above (alternate syntax)
function myfunc() {
    echo "hello $1"
}
myfunc "John"

# Returning values
myfunc() {
    local myresult='some value'
    echo $myresult
}
result="$(myfunc)"

# Raising errors
myfunc() {
  return 1
}
if myfunc; then
  echo "success"
else
  echo "failure"
fi

Arguments

$#  # Number of arguments
$*  # All arguments
$@  # All arguments, starting from first
$1  # First argument

Conditionals

Conditions

Note that [[ is actually a command/program that returns either 0 (true) or 1 (false). Any program that obeys the same logic (like all base utils, such as grep(1) or ping(1)) can be used as condition, see examples.
[[ -z STRING ]] # Empty string
[[ -n STRING ]] # Not empty string
[[ STRING == STRING ]]  # Equal
[[ STRING != STRING ]]  # Not Equal
[[ NUM -eq NUM ]]   # Equal
[[ NUM -ne NUM ]]   # Not equal
[[ NUM -lt NUM ]]   # Less than
[[ NUM -le NUM ]]   # Less than or equal
[[ NUM -gt NUM ]]   # Greater than
[[ NUM -ge NUM ]]   # Greater than or equal
[[ STRING =~ STRING ]]  # Regexp
(( NUM < NUM )) # Numeric conditions
[[ -o noclobber ]]  # If OPTIONNAME is enabled
[[ ! EXPR ]]    # Not
[[ X ]] && [[ Y ]]  # And
[[ X ]] || [[ Y ]]  # Or

# File conditions
[[ -e FILE ]]   # Exists
[[ -r FILE ]]   # Readable
[[ -h FILE ]]   # Symlink
[[ -d FILE ]]   # Directory
[[ -w FILE ]]   # Writable
[[ -s FILE ]]   # Size is > 0 bytes
[[ -f FILE ]]   # File
[[ -x FILE ]]   # Executable
[[ FILE1 -nt FILE2 ]]   # 1 is more recent than 2
[[ FILE1 -ot FILE2 ]]   # 2 is more recent than 1
[[ FILE1 -ef FILE2 ]]   # Same files

Example

if ping -c 1 google.com; then
  echo "It appears you have a working internet connection"
fi
if grep -q 'foo' ~/.bash_history; then
  echo "You appear to have typed 'foo' in the past"
fi

# String
if [[ -z "$string" ]]; then
  echo "String is empty"
elif [[ -n "$string" ]]; then
  echo "String is not empty"
fi

# Combinations
if [[ X ]] && [[ Y ]]; then
  ...
fi

# Equal
if [[ "$A" == "$B" ]]

# Regex
if [[ "A" =~ "." ]]
if (( $a < $b )); then
   echo "$a is smaller than $b"
fi
if [[ -e "file.txt" ]]; then
  echo "file exists"
fi

Arrays

# Defining arrays
Fruits=('Apple' 'Banana' 'Orange')
Fruits[0]="Apple"
Fruits[1]="Banana"
Fruits[2]="Orange"

# Working with arrays
echo ${Fruits[0]}           # Element #0
echo ${Fruits[@]}           # All elements, space-separated
echo ${#Fruits[@]}          # Number of elements
echo ${#Fruits}             # String length of the 1st element
echo ${#Fruits[3]}          # String length of the Nth element
echo ${Fruits[@]:3:2}       # Range (from position 3, length 2)

# Operations
Fruits=("${Fruits[@]}" "Watermelon")    # Add
Fruits+=('Watermelon')                  # Also add
Fruits=( ${Fruits[@]/Ap*/} )            # Remove by regex match
unset Fruits[2]                         # Remove one item
Fruits=("${Fruits[@]}")                 # Duplicate
Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate
lines=(`cat "logfile"`)                 # Read from file

# Iteration
for i in "${arrayName[@]}"; do
  echo $i
done

Dictionaries

# Defining
declare -A sounds
sounds[dog]="bark"
sounds[cow]="moo"
sounds[bird]="tweet"
sounds[wolf]="howl"
# Declares sound as a Dictionary object (aka associative array).

# Working with dictionaries
echo ${sounds[dog]} # Dog's sound
echo ${sounds[@]}   # All values
echo ${!sounds[@]}  # All keys
echo ${#sounds[@]}  # Number of elements
unset sounds[dog]   # Delete dog

Iteration

# Iterate over values
for val in "${sounds[@]}"; do
  echo $val
done

# Iterate over keys
for key in "${!sounds[@]}"; do
  echo $key
done

Options

# Options
set -o noclobber  # Avoid overlay files (echo "hi" > foo)
set -o errexit    # Used to exit upon error, avoiding cascading errors
set -o pipefail   # Unveils hidden failures
set -o nounset    # Exposes unset variables

# Glob options
set -o nullglob    # Non-matching globs are removed  ('*.foo' => '')
set -o failglob    # Non-matching globs throw errors
set -o nocaseglob  # Case insensitive globs
set -o globdots    # Wildcards match dotfiles ("*.sh" => ".foo.sh")
set -o globstar    # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb')
# Set GLOBIGNORE as a colon-separated list of patterns to be removed from glob matches.

History

# Commands
history # Show history
shopt -s histverify # Don’t execute expanded result immediately
# Expansions
!$  # Expand last parameter of most recent command
!*  # Expand all parameters of most recent command
!-n # Expand nth most recent command
!n  # Expand nth command in history
!<command>  # Expand most recent invocation of command <command>
# Operations
!!  # Execute last command again
!!:s/<FROM>/<TO>/   # Replace first occurrence of <FROM> to <TO> in most recent command
!!:gs/<FROM>/<TO>/  # Replace all occurrences of <FROM> to <TO> in most recent command
!$:t    # Expand only basename from last parameter of most recent command
!$:h    # Expand only directory from last parameter of most recent command
!! and !$ # can be replaced with any valid expansion.

# Slices
!!:n    # Expand only nth token from most recent command (command is 0; first argument is 1)
!^  # Expand first argument from most recent command
!$  # Expand last token from most recent command
!!:n-m  # Expand range of tokens from most recent command
!!:n-$  # Expand nth token to last from most recent command
!! # can be replaced with any valid expansion i.e. !cat, !-2, !42, etc.

Miscellaneous

# Numeric calculations
$((a + 200))      # Add 200 to $a
$((RANDOM%=200))  # Random number 0..200

# Subshells
(cd somedir; echo "I'm now in $PWD")
pwd # still in first directory
# Redirection
python hello.py > output.txt   # stdout to (file)
python hello.py >> output.txt  # stdout to (file), append
python hello.py 2> error.log   # stderr to (file)
python hello.py 2>&1           # stderr to stdout
python hello.py 2>/dev/null    # stderr to (null)
python hello.py &>/dev/null    # stdout and stderr to (null)
python hello.py < foo.txt      # feed foo.txt to stdin for python
# Inspecting commands
command -V cd
#=> "cd is a function/alias/whatever"
# Trap errors
trap 'echo Error at about $LINENO' ERR
or

traperr() {
  echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}"
}

set -o errtrace
trap traperr ERR

# Case/switch
case "$1" in
  start | up)
    vagrant up
    ;;

  *)
    echo "Usage: $0 {start|stop|ssh}"
    ;;
esac

# Source relative
source "${0%/*}/../share/foo.sh"
printf
printf "Hello %s, I'm %s" Sven Olga
#=> "Hello Sven, I'm Olga

printf "1 + 1 = %d" 2
#=> "1 + 1 = 2"

printf "This is how you print a float: %f" 2
#=> "This is how you print a float: 2.000000"

# Directory of script
DIR="${0%/*}"

# Getting options
while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in
  -V | --version )
    echo $version
    exit
    ;;
  -s | --string )
    shift; string=$1
    ;;
  -f | --flag )
    flag=1
    ;;
esac; shift; done
if [[ "$1" == '--' ]]; then shift; fi

# Heredoc
cat <<END
hello world
END

# Reading input
echo -n "Proceed? [y/n]: "
read ans
echo $ans
read -n 1 ans    # Just one character

# Special variables
$?  # Exit status of last task
$!  # PID of last background task
$  # PID of shell
$0  # Filename of the shell script

# Go to previous directory
pwd # /home/user/foo
cd bar/
pwd # /home/user/foo/bar
cd -
pwd # /home/user/foo

HOTKEYS

Редактирование команд bash

Ctrl + a – перейти на начало командной строки

Ctrl + e – перейти в конец командной строки

Ctrl + k – удалить от курсора до конца командной строки

Ctrl + u – удалить от курсора до начала командной строки

Ctrl + w – удалить от курсора до начала слова

Ctrl + y – вставить слово или текст который был вырезано с использованием одного из подходящих сочетаний клавиш

Ctrl + xx – переместиться между началом командной строки и текущим положением курсора (работает в обе стороны)

Alt + b – переместить курсор на одно слово назад (или переместиться на начало текущего слова)

Alt + f – переместиться вперед на одно слово (или к началу текущего слова)

Alt + d – удалить до конца слова начиная с текущего положения курсора (все слово, если курсор находится в его начале)

Alt + c – сделать букву заглавной и переместиться в конец слова

Alt + u – сделать все буквы большими до конца слова

Alt + l – сделать все буквы маленькими до конца слова

Alt + t – поменять местами текущее слово и предыдущее

Ctrl + f – переместиться вперед на один символ

Ctrl + b – переместиться назад на один символ

Ctrl + d – удалить символ под курсором

Ctrl + h – удалить символ перед курсором

Ctrl + t – поменять местами символ под курсором и предыдущий.

Повторный вызов команд bash

Ctrl + r – искать в истории команд

Ctrl + g – выйти из режима поиска команд

Ctrl + p – перейти к предыдущей команде в истории

Ctrl + n – перейти к следующей команде в истории

Alt + . – использовать последнее слово из предыдущей команды.

Управление работой команд bash

Ctrl + l – очистить экран

Ctrl + s – остановить вывод на экран (для долго выполняющихся команд с расширенным выводом)

Ctrl + q – продолжить вывод на экран

Ctrl + c – остановить работу команды

Ctrl + z – приостановить работу команды.

Команды с восклицательным знаком (Bash Bang)

!! – выполнить предыдущую команду

!blah – выполнить последнюю команду начинающуюся с blah (к примеру, !ls)

!blah:p – вывести в консоль текст команды, которую бы выполнила команда !blah(также добавить эту команду в конец истории)

!$ – последнее слово предыдущей команды (то же самое, что и Alt + .)

!$:p – вывести в консоль текст, который бы заменила команда !$

!* – предыдущая команда за исключением первого слова (к примеру, если предыдущая команда была vi cd /etc, то !* выполнит cd /etc)

!*:p – вывести в консоль текс, который бы заменила команда !*.

С помощью символов ^^ мы можем выполнить предыдущую команду с заменой части текста на другой. К примеру:

ps a
^a^aux
ls aux

Таким образом содержимое между "крышечками" заменяется на содержимое, которое указано после них.