All notes

My bashrc

## Suppress pushd output.
pushd () {
    command pushd "[email protected]" > /dev/null
popd () {
    command popd "[email protected]" > /dev/null

w_run() {
  for i in `seq $number`; do
    [email protected]
export w_run
# Example: w_run 4 echo "Hehe"

# bash checks the window size after each command and, if necessary, updates the values of LINES and COLUMNS.
shopt -s checkwinsize
shopt -s cmdhist
# Do not record duplicate commands, ls, bg, fg and exit.
export HISTIGNORE="&:ls:[bf]g:exit"

########## Used for git branch tracking.
find_git_branch() {
  # Based on:
  local branch
  if branch=$(git rev-parse --abbrev-ref HEAD 2> /dev/null); then
    if [[ "$branch" == "HEAD" ]]; then
find_git_dirty() {
  local status=$(git status --porcelain 2> /dev/null)
  if [[ "$status" != "" ]]; then
PROMPT_COMMAND="find_git_branch; find_git_dirty; $PROMPT_COMMAND"

# The $PWD must be capitalized and the $ must be escaped, otherwise the $PWD will be evaluated where the PS1 is assigned, and then the PS1 will always show the home directory.
export PS1="\[email protected]\h:\w \$git_branch\$git_dirty"
# The last color-set sequence, \[\e[1;37m\], is not closed, so the remaining text will be white.
# The color setting may cause problem. Use in caution.
# export PS1='\[\e[0;32m\]\h\[\e[m\]:\[\e[1;34m\]\w\[\e[m\] \[\e[1;32m\]\u\$\[\e[m\] \[\e[1;37m\]'

export EDITOR=emacs

# alias open="xdg-open"
# export open

export PATH=$PATH:~/bin
export TERM='xterm-color'

cdls() { cd "[email protected]" && ls; }

alias ls="ls --color" # Linux
# alias ls="ls -G" # Mac
export ls
alias ll='ls -al'
export ll
alias rm='rm -ir'
export rm
alias rmf='rm -fr'
export rmf
alias cp='cp -ir'
export cp
alias mv='mv -i'
export mv
alias scp='scp -r'
export scp
alias grep="grep --color=always -n"
export grep


\u - Username. The original prompt also has \h, which prints the host name.
\w - Current absolute path. Use \W for current relative path.
\$ - The prompt character (eg. # for root, $ for regular users).
\[ and \] - These tags should be placed around color codes so bash knows how to properly place the cursor.

Colorful PS1



\e[ Indicates the beginning of color prompt
x;ym Indicates color code. Use the color code values mentioned below.
\e[m indicates the end of color prompt

Black 0;30
Blue 0;34
Green 0;32
Cyan 0;36
Red 0;31
Purple 0;35
Brown 0;33
[Note: Replace 0 with 1 for dark color]


tldp: terminfo. Terminfo (formerly Termcap, "cap" - "capability") is a database of terminal capabilities and more. To move the cursor to row 3, col 6 it simply calls: move(3,6) (part of ncurses). A Linux package that doesn't require ncurses may still need a terminfo file for your terminal.


Repeat like in zsh

# If you're using the zsh shell:
repeat 10 { echo 'Hello' }

w_run() {
  for i in `seq $number`; do
    [email protected]
export w_run
# Example: w_run 4 echo "Hehe"

seq 10 | xargs -Iz "Hello z" # z will be 1..10

Bash script recommendations

A function to print out error messages along with other status information is recommended. The following is from Google shell script guide.

err() {
	echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: [email protected]" >&2

Replacement of getopts

StackOverflow. It says:

Never use getopt. getopt cannot handle empty arguments strings, or arguments with embedded whitespace.
${i#*=} search for "Substring Removal" in this guide. It is functionally equivalent to `sed 's/[^=]*=//' <<< "$i"` which calls a needless subprocess or `echo "$i" | sed 's/[^=]*=//'` which calls two needless subprocesses.

for i in "[email protected]"
case $i in
    EXTENSION="${i#*=}" # Here only the substr after '=' remains.
    shift # past argument=value
    shift # past argument=value
    shift # past argument=value
    shift # past argument with no value
            # unknown option
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 $1

Straight Bash Space Separated

Usage: ./ -e conf -s /etc -l /usr/lib /etc/hosts

# Use > 1 to consume two arguments per pass in the loop (e.g. each
# argument has a corresponding value to go with it).
# Use > 0 to consume one or more arguments per pass in the loop (e.g.
# some arguments don't have a corresponding value to go with it such
# as in the --default example).
# note: if this is set to > 0 the /etc/hosts part is not recognized ( may be a bug )
while [[ $# > 1 ]]

case $key in
    shift # past argument
    shift # past argument
    shift # past argument
            # unknown option
shift # past argument or value
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 $1

Cheet sheet

HereDoc here-docs.

# Basic.
interactive-program <<LimitString
command #1
command #2

# Here variables are expanded.
cat <<Endofmessage
Hello, there, $NAME.
Greetings to you, $NAME, from $RESPONDENT.
# This comment shows up in the output (why?).

# You can't use "echo <<EOT", because echo does not 'by default' read from the stdin descriptor (
# E.g. you can't either: cat "hello"
# You can use 'echo' in this way:
echo "\

# With single/double quotes or backslash escape, the variables will not expanded.
cat >> path/to/file/to/append-to.txt << "EOF"
export PATH=$HOME/jdk1.8.0_31/bin:$PATH
export JAVA_HOME=$HOME/jdk1.8.0_31/

# '-': Suppresses leading tabs (but not spaces)
	This is line 1 of the message.
	This is line 2 of the message.
	This is line 3 of the message.
	This is line 4 of the message.
	This is the last line of the message.

# Set a variable from HereDoc.
variable=$(cat <<"SETVAR"
This variable
runs over multiple lines.
echo "$variable"

# "anonymous" here document
# use : as a dummy command.
${HOSTNAME?}${USER?}${MAIL?}  # Print error message if one of the variables not set.

Use heredoc for comments/documentations:


if [ "$1" = "-h"  -o "$1" = "--help" ]     # Request help.
then                                       # Use a "cat script" . . .
List the statistics of a specified directory in tabular format.
The command-line parameter gives the directory to be listed.
If no directory specified or directory specified cannot be read,
then list the current working directory.



Conditional expressions

-e file, -a file
       True if file exists.

-r file
       True if file exists and is readable.
-w file
       True if file exists and is writable.
-x file
       True if file exists and is executable.

-s file
       True if file exists and has a size greater than zero.
-N file
       True if file exists and has been modified since it was last read.

-f file
       True if file exists and is a regular file.
-d file
       True if file exists and is a directory.
-L file, -h file
       True if file exists and is a symbolic link.
-b file
       True if file exists and is a block special file.
-c file
       True if file exists and is a character special file.
-p file
       True if file exists and is a named pipe (FIFO).
-S file
       True if file exists and is a socket.

-g file
       True if file exists and is set-group-id.
-k file
       True if file exists and its ``sticky'' bit is set.
-u file
       True if file exists and its set-user-id bit is set.
-O file
       True if file exists and is owned by the effective user id.
-G file
       True if file exists and is owned by the effective group id.

-t fd  True if file descriptor fd is open and refers to a terminal.

-o optname
       True if shell option optname is enabled.  See the list of options under the description of the -o option to the set builtin below.
-z string
       True if the length of string is zero.
-n string
       True if the length of string is non-zero.

tldp: comparison ops.

Ways to AND expressions:

if [ ! -z "$var" ] && [ -e "$var" ]; then
	# something ...

if [[ -n "$var" && -e "$var" ]] ; then
    echo "'$var' is non-empty and the file exists"

if [ -n "$var" -a -e "$var" ]; then
    do something ...

# Note that the "<" needs to be escaped within a [ ] construct.
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ]

if which ruby >/dev/null && which gem >/dev/null; then
    PATH="$(ruby -rubygems -e 'puts Gem.user_dir')/bin:$PATH"
# /Users/me/.gem/ruby/2.1.0

## A one liner:
if [[ -d bin  ]]; then echo "haha"; fi
EOT; echo htmlspecialchars($str); ?>

Why we need a space after [


In old days [ was a command which has same function for test. it is currently a builtin-command but [ residues in /usr/bin/[. so test $a -gt 27 ] is good but test$a -gt 27] is bad. as same for [$a.



for arg in "$var1" "$var2" "$var3" ... "$varN"  
# In pass 1 of the loop, arg = $var1	    
# In pass 2 of the loop, arg = $var2	    
# In pass 3 of the loop, arg = $var3	    
# ...
# In pass N of the loop, arg = $varN

# Arguments in [list] quoted to prevent possible word splitting.



for file in $FILES
  if [ ! -e "$file" ]       # Check if file exists.
    echo "$file does not exist."; echo
    continue                # On to next.

  ls -l $file | awk '{ print $8 "         file size: " $5 }'  # Print 2 fields.
  whatis `basename $file`   # File info.
  # Note that the whatis database needs to have been set up for this to work.
  # To do this, as root run /usr/bin/makewhatis.

for i in $(ls *.$SUFF)
  mv -f $i ${i%.$SUFF}.$suff
  #  Leave unchanged everything *except* the shortest pattern match
  #+ starting from the right-hand-side of the variable $i . . .
done ### This could be condensed into a "one-liner" if desired.

# List contents of all shells under current dir:
for file in $filename
	echo "Contents of $file"
	echo "---"
	cat "$file"

# Removes only files beginning with "j" or "x" in $PWD.

for file in [jx]*
	rm -f $file
	echo "Removed file \"$file\"".

# Invoke this script both with and without arguments, and see what happens.

for a
# echo -n: Do not print the trailing newline character.
	echo -n "$a "

# The 'in list' missing, therefore the loop operates on '[email protected]' (command-line argument list, including whitespace).


# Locates matching strings in a binary file.
# A "grep" replacement for binary files.
# Similar effect to "grep -a"


if [ $# -ne 2 ]
  echo "Usage: `basename $0` search_string filename"
  exit $E_BADARGS

if [ ! -f "$2" ]
  echo "File \"$2\" does not exist."
  exit $E_NOFILE

IFS=$'\012'       # Per suggestion of Anton Filippov. was:  IFS="\n"
for word in $( strings "$2" | grep "$1" )
# The "strings" command lists strings in binary files.
# Output then piped to "grep", which tests for desired string.
  echo $word

# As S.C. points out, lines 23 - 30 could be replaced with the simpler: strings "$2" | grep "$1" | tr -s "$IFS" '[\n*]'

#  Try something like  "./ mem /bin/ls"

exit 0

# The result of for loop could be piped to other commands.

for file in "$( find $directory -type l )"   # -type l = symbolic links
  echo "$file"
done | sort                                  # Otherwise file list is unsorted.
#  Strictly speaking, a loop isn't really necessary here,
#+ since the output of the "find" command is expanded into a single word.
#  However, it's easy to understand and illustrative this way.

# Iterates through numbers.

# Standard syntax.
for a in 1 2 3 4 5 6 7 8 9 10
  echo -n "$a "

echo; echo

# +==========================================+

# Using "seq" ...
for a in `seq 10`
  echo -n "$a "

echo; echo

# +==========================================+

# Using brace expansion ...
# Bash, version 3+.
for a in {1..10}
  echo -n "$a "

echo; echo

# +==========================================+

# Now, let's do the same, using C-like syntax.

for ((a=1; a <= LIMIT ; a++))  # Double parentheses, and naked "LIMIT"
  echo -n "$a "


while true; do python; done


Special variables

*      Expands to the positional parameters, starting from one. "$*" is equivalent to "$1c$2c...", where c is the first character of the value of the IFS variable. If IFS is unset, the parameters are separated by spaces. If IFS is null, the parameters are joined without intervening separators.
@      Expands to the positional parameters, starting from one. "[email protected]"  is equivalent  to "$1" "$2" ...
#      Expands to the number of positional parameters in decimal.
-      Expands to the current option flags as specified upon invocation, by the set builtin command, or those set by the shell itself (such as the -i option).
$      Expands to the process ID of the shell.  In a () subshell, it expands to the process ID of the current shell, not the subshell.
!      Expands to the process ID of the most recently executed background (asynchronous) command.
?      Expands to the status of the most recently executed foreground pipeline.
0      Expands  to the name of the shell or shell script. If bash is invoked with a file of commands, $0 is set to the name of that file.
_      At  shell  startup,  set  to  the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list.

echo $*
echo [email protected]
echo $#
echo $-
echo $$
echo $!
echo $?
echo $0
echo $_

"./ a b c". Result:
a b c
a b c


http_proxy, no_proxy

export http_proxy=
export https_proxy=
export no_proxy=localhost,,*.local

Not working?

wcfNote: ping or telnet may fail to test the http connectivity. Use "wget" instead.

git http.proxy

function proxyGit() {
  git config --global http.proxy http://proxy.mycompany:8080

function proxyGitOff() {
  git config --global unset http.proxy

git config --global --get http.proxy

Little case

stackExchange: what's the right format for the http_proxy.

There is no central authority who assigns an official meaning to environment variables before applications can use them. POSIX defines the meaning of some variables (PATH, TERM, …) and lists several more in a non-normative way as being in common use, all of them in uppercase. http_proxy and friends isn't one of them.

Unlike basically all conventional environment variables used by many applications, http_proxy, https_proxy, ftp_proxy and no_proxy are commonly lowercase. I don't recall any program that only understands them in uppercase, I can't even find one that tries them in uppercase. Many programs use the lowercase variant only, including lynx, wget, curl, perl LWP, perl WWW::Search, python urllib/urllib2, etc. So for these variables, the right form is the lowercase one.

The lowercase name dates back at least to CERN libwww 2.15 in March 1994 (thanks to Stéphane Chazelas for locating this). I don't know what motivated the choice of lowercase, which would have been unusual even then.


  An  array variable containing a list of exit status values from the processes in the most-recently-executed foreground pipeline (which may contain only a single command).


The order of expansions is:

  1. brace expansion;
  2. tilde expansion;
  3. parameter and variable expansion;
  4. arithmetic expansion;
  5. command substitution (done in a left-to-right fashion);
  6. word splitting;
  7. filename expansion.

Brace expansion


# Notice that there are no spaces inside the brackets or between the brackets and the adjoining strings.
echo {one,two,red,blue}fish
# onefish twofish redfish bluefish
echo {one, two, red, blue }fish
# {one, two, red, blue }fish
echo "{one,two,red,blue} fish"
# {one,two,red,blue} fish

# Spaces are OK if they're enclosed in quotes outside the braces or within an item in the comma-separated list
echo {"one ","two ","red ","blue "}fish
# one fish two fish red fish blue fish
echo {one,two,red,blue}" fish"
# one fish two fish red fish blue fish

cp /etc/httpd/conf/httpd.conf{,.bak}

diff /etc/httpd/conf/httpd.conf{.bak,}

Tilde expansion

# Get $PWD.
echo ~+
# Get $OLDPWD.
echo ~-

# The subdirectory `foo' of the home directory of the user `fred'.
echo ~fred/foo

Shell parameter expansion parameter substitution.

########## ${parameter-default}, ${parameter:-default}
# If parameter not set, use default.

# var3 is unset.

echo ${var1-$var2}   # 1
echo ${var3-$var2}   # 2
#           ^          Note the $ prefix.

echo ${username-`whoami`}
# Echoes the result of `whoami`, if variable $username is still unset.

##### The extra : makes a difference only when parameter has been declared, but is null.
# variable has been declared, but is set to null.

echo "${variable-0}"    # (no output)
echo "${variable:-1}"   # 1
#               ^

unset variable

echo "${variable-2}"    # 2
echo "${variable:-3}"   # 3

########## ${parameter=default}, ${parameter:=default}
# If parameter not set, set it to default.

echo ${var=abc}   # abc
echo ${var=xyz}   # abc
# $var had already been set to abc, so it did not change.

echo "hello $WORLD" # hello
echo "hello ${WORLD:-world}" # hello world
echo "hello ${WORLD}" # hello
echo "hello ${WORLD:=world}" # hello world
echo "hello ${WORLD}" # hello world

########## ${parameter+alt_value}, ${parameter:+alt_value}
# If parameter set, use alt_value, else use null string.

echo "a = $a"      # a = xyz

########## ${parameter?err_msg}, ${parameter:?err_msg}
# If parameter set, use it, else print err_msg and abort the script with an exit status of 1.

${ZZXy23AB?"ZZXy23AB has not been set."}

${1?"Usage: $0 ARGUMENT"}
# Script exits here if command-line parameter absent.

########## ${#var}

# ${#var} String length.
# For an array, ${#array} is the length of the first element in the array.
# ${#*} and ${#@} give the number of positional parameters.
# For an array, ${#array[*]} and ${#array[@]} give the number of elements in the array.

########## ${var#Pattern}, ${var##Pattern}
# ${var#Pattern} Remove from $var the shortest part of $Pattern that matches the front end of $var.
# ${var##Pattern} Remove the longest.

# Strip possible leading zero(s) from argument
strip_leading_zero ()
  echo "${1#0}"
# 000haha -> 00haha

Variable contains space

Variable expansion in quotes and not in quotes makes difference:

a="d d"
set -x

# Without quotes.
echo $a
# + echo d d
# d d

# With quotes, the 'd d' is passed as a string.
echo test "$a"
# + echo 'd d'
# d d



The contents of this variable are executed as a regular Bash command just before Bash displays a prompt.

SHLVL why would you use shlvl.

echo $SHLVL
# 1

# Enter a subshell.
echo $SHLVL
# 2

Side note: "export" will make variables in parent shells available to its sub-shells. But, "export" in sub-shells will not be avail in parent shells.



# Assignment
array=(var1 var2 var3 ... varN)
# or
array=([0]=var1 [1]=var2 [2]=var3 ... [n]=varN)
# or

# Get element
echo ${array[0]}

# Erase element
unset array[2]
# Erase whole array
unset array
# Print the variable
set | grep arrayName

# Iterate
for var in ${filename[@]};do
	echo $var
# Get the total number:
echo ${#array[@]}
# or
echo ${#array[*]}

Builtin commands


SO: check if a program exists:

command [-pVv] command [arg ...]
  Run command with args suppressing the normal shell function lookup. Only builtin commands or commands found in the PATH are executed.

command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

hash wich
# 1
hash which
# 0

command -v which
# /usr/bin/which
command -v ls
# alias ls='ls -G'


The positional parameters are shifted to the left by this number, N. If N is not present, it is assumed to be 1.

while [[ "$#" > 0 ]]; do
  echo "$1"

shopt shopt builtin.

# Print all options which are unset/set.
shopt -u/-s

# Optnames:

# autocd
# If set, a command name that is the name of a directory is executed as if it were the argument to the cd command. This option is only used by interactive shells.

# expand_aliases
# If set, aliases are expanded as described below under Aliases, Aliases. This option is enabled by default for interactive shells.

# pipefail
# The exit status of a pipeline is the exit status of the last command in the pipeline, unless the pipefail option is enabled.
# If pipefail is enabled, the pipeline’s return status is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully. wcfNote: this option makes checking more stringent.




function function_name {
	cmd $1 $2

########## C-like

# NOTE: the parenthesis can't contain any params.
# So "function_name (arg1, arg2)" will not work.
function_name () {
	cmd $1 $2

function_name "arg1" "arg2"

######## One liner

fun () { echo "This is a function"; echo; }

Export function

SO: call a function defined in bashrc.

function hello() {
   echo "Hello, $1!"

export -f hello

Return string from a function

function __capitalizeFirstChar()
  echo `echo "${1:0:1}" | tr a-z A-Z`${1:1}

function __migrate() {
  env=$(__capitalizeFirstChar "$1")

  docker exec -ti fenhe$env python3 migrate
  docker exec -ti fenhe$env python3 collectstatic --noinput




case "$1" in
		status anacron


		if test "x`pidof anacron`" != x; then
		echo $"Usage: $0 {start|stop|restart|condrestart|status}"
		exit 1


# This script does a very simple test for checking disk space.

space=`df -h | awk '{print $5}' | grep % | grep -v Use | sort -n | tail -1 | cut -d "%" -f1 -`

case $space in
# 10%-69%
  Message="All is quiet."
# 70%-89%
  Message="Start thinking about cleaning out some stuff.  There's a partition that is $space % full."
# 91-98%
  Message="Better hurry with that new disk...  One partition is $space % full."
# 99%
  Message="I'm drowning here!  There's a partition at $space %!"
  Message="I seem to be running with an nonexistent amount of disk space..."

echo $Message | mail -s "disk report `date`" anny

Use tail -16 /var/spool/mail/anny to see the email.



# var1 will have the result string.
var1=$(id user)



alias ll="ls -l"
unalias ll

# Removes All aliases
unalias -a


# List current limit for core dump.
ulimit -c

# Set unlimited for core dump.
ulimit -c unlimited


Print input lines: -v/-x


Useful for debugging.

# -v, -o verbose	Print shell input lines as they are read.
# -x, -o xtrace		Print input lines with variables expanded. "+" is preceded before each line as an indicator.
set -x

Exit on error: -e

Same: -o errexit.

# The false command causes the subshell to exit without executing echo one;
# however, echo two is executed because the exit status of the pipeline (false; echo one) | cat is zero.
set -e; (false; echo one) | cat; echo two
# two

for planet in "Mercury 36" "Venus 67" "Earth 93"  "Mars 142" "Jupiter 483"
  set -- $planet  #  Parses variable "planet" and sets positional parameters.
  #  The "--" prevents nasty surprises if $planet is null or begins with a dash.

  #  May need to save original positional parameters, since they get overwritten.
  #  One way of doing this is to use an array,
  #         original_params=("[email protected]")

  echo "$1		$2,000,000 miles from the sun"
  #-------two  tabs---concatenate zeroes onto parameter $2
How to ignore error

#!/bin/bash -le

killall haha_killme || echo "Ignore an error."



set -u
rm -r $chroot/usr/share
# Guanrantee that if ${chroot} is not defined, it will not execute like this: "rm -r /usr/share".

# wcfNote: use this sparingly. For example, the following will complain if $1 is not set:
[ -n "$1" ] && do_something



-n	do not print the trailing newline.
-e	enable interpretation of backslash escapes.

echo -e "This is a test.\nAnother line.\nNow comes the table:\ncol1\tcol2"

# Make sure to avoid quotes inside quotes.
echo 'deb blah ... blah' | sudo tee --append /etc/apt/sources.list

# To avoid printing data back to the console:
echo 'deb blah ... blah' | sudo tee --append /etc/apt/sources.list > /dev/null


# Run cmd on sig1 and sig2.
trap cmd sig1 sig2
# Clean up tmp when shell ends or exits.
trap clean_tmp INT TERM EXIT

# Ignore sig1
trap "" sig1
# Reenable sig1
trap - sig1


Export to the environment of subsequently executed commands.

export [-fn] [name[=word]] ...
export -p

[-p]	display a list of exported names.
-f	The names refer to shell functions; otherwise the names refer to shell variables
-n	No longer mark each name for export.

Export not working to change the environment from a bash script?

StackOverflow. Child processes cannot alter their parent's environment. The parent has to choose to alter itself (either with source or eval). StackExchange.

# Method 1.
# sourcefile
export JAVA_HOME=/cygdrive/c/dev/Java/jdk1.5.0_22
export PATH=$JAVA_HOME/bin:$PATH
# and then source it
source sourcefile

# Method 2.
echo "export JAVA_HOME=/cygdrive/c/dev/Java/jdk1.5.0_22"
echo "export PATH=$JAVA_HOME/bin:$PATH"
# and then evaluate it:
eval ``


eval takes its arguments, concatenates them separated by spaces, and executes the resulting string as Bash code in the current execution environment.
eval in Bash works in essentially the same way as most other languages that have an eval function.
Perhaps the easiest way to think about eval is that it works in the same way as running bash -c "bash code…" from a script, except in the case of eval, the given code is executed in the current shell environment rather than a child process.

for i in 2 3 4; do
 eval a$i=2
 eval echo "\$a$i" 
# Output:
# 2
# 2
# 2

for i in 2 3 4; do
 eval a$i=2
 eval echo "$a$i" 
# Output:
# $a2
# $a3
# $a4


"xargs" is designed to construct argument lists and invoke other utility. xargs.

xargs functionality can be achived using the backquote feature of shell. But, it offers more options.

echo 1 2 3 4 | xargs

# Use first two argument per command:
echo 1 2 3 4 | xargs -n 2
# 1 2
# 3 4

# Find all .bak files in or below the current directory and delete them.
find . -name "*.bak" -type f -print | xargs /bin/rm -f

# Find all .bak files in or below the current directory and move them to ~/.old.files directory:
find . -name "*.bak" -print0 | xargs -0 -I {} mv {} ~/old.files
# You can rename {} to something else. In the following example {} is renamed as file. This is more readable as compare to previous example:
find . -name "*.bak" -print0 | xargs -0 -I file mv file ~/old.files

# To copy all media files to another location called /bakup/iscsi, you can:
cp -r -v -p /share/media/mp3/ /backup/iscsi/mp3
# However, cp command may fail if an error occurs such as if the number of files is too large for the cp command to handle. xargs in combination with find can handle such operation nicely. xargs is more resource efficient and will not halt with an error:
find /share/media/mp3/ -type f -name "*.mp3" -print0 | xargs -0 -r -I file cp -v -p file --target-directory=/bakup/iscsi/mp3


See man bash, redirection section for detail:

# This is preferred:
app > res.txt 2>&1
# This is problematic:
app 2>&1 > res.txt 

# Explanation:
# This directs only the standard output to file dirlist, because the standard error was duplicated as standard output, before the standard output was redirected to dirlist.
ls 2>&1 > dirlist

# Redirect stderr to stdout and then stdout to /dev/null:
# wcfNote: so stdout is discarded but stderr is greped???
command 2>&1 >/dev/null | grep 'something'


How to do mathematics in BASH?
For integers,

  1. Arithmetic expansion: \$((EXPR)). Example: num=\$((num+1)), or num=\$((num+=1)). This is a POSIX standard.
  2. Expr utility. Example: num=`expr \$num + 1`. It is a bit slower than the arithmetic expansion.
For floats: num=\$(echo \$num + 1 | bc).
Important notes:
  1. When doing assignment, there should be no whitespace around =. Otherwise the shell will interprete the first word as the name of application to run. Wrong: num= 1, or num =1. Right: num=1.
  2. Both bc and expr expect each number and operator as a separated argument, so whitespace is critical. Wrong: num=`expr \$num+1`. Right: num=`expr \$num + 1`.
Reference here.


# Calculate the division and display the result with 5 digits after decimal point.
# Result: 1.25714
echo "scale=5; 0.88 / 0.7" | bc

Declare, typeset

These commands are used to constrain the attribute of variables, e.g. declare one as integer.

To find the doc for declare, you can go to MANpage of bash and read the section BUILTINS. declare -f func is used to print the function definition, if the function is un-defined, it returns error 1 (check with echo $?).

Other examples:


declare -i number
# 脚本余下的部分会把"number"当作整数看待.
echo "Number = $number"     # Number = 3
echo "Number = $number"     # Number = 0
# 脚本尝试把字符串"three"作为整数来求值(译者注:当然会失败,所以出现值为0).

# Do automatic arithmatics.
echo "n = $n"       # n = 6/3
declare -i n
echo "n = $n"       # n = 2


$?	Exit status.
$$	ProcessID of the script itself. The $$ variable often finds use in scripts to construct "unique" temp file names. This is usually simpler than invoking mktemp.

Ref: unset var.


// Set to empty
// Another way to set it to empty.

// Delete the var.
unset var



history 100 # Show the last 100 commands in history.
!95	# Execute the 95th command in history.
!-5 # Execute the previously 5th command.
!! 	# Execute the last command. Same as !-1.
ls -la # a command example.
!l	# Search the command starting with "l". Will execute the ls -la.
!?ls? # Search the command containing the string "ls".
^str1^str2^ # Repeat the last command, replace str1 with str2. Equivalent to !!:s/str1/str2.
!ls:p	# :p is a modifier. This will only print the command but not execute it.
history | grep -i ls	# Combo with grep.

Another good way to use history productively is by typing Ctrl-r (search backwards in history) in the terminal, type and edit the command and then run. Reference in stackoverflow. For complete information, type man bash and find HISTORY EXPANSION.

This reference provides some customizations.


Find out how to make detach from terminal.


jobs -l # list all jobs. &
disown -h # Disown all jobs and let them stand with SigHUP.

nohup & # Run in background without worring about sigHUP.

nohup nice -n -5 &

echo "" | at now + 1 minute # Make to queue later execution.

Conditional expressions

-b file	:	true if file is a block special file.
-c file	:	true if file is a char special file.
-d file	:	true if file is a dir.
-f file	:	true if file is a regular file.
-h/-L file	:	true if file is a symbolic link.
-p file	:	true if file is a named pipe (FIFO).
-t file	:	true if file descriptor is open and refers to a terminal.
-S file	:	true if file is a socket.

-g file	:	true if file is set-group-id.
-k file	:	true if file's sticky bit is set.
-u file	:	true if file's set-user-id bit is set.

-a/-e file	:	true if file exists.
-s file	:	true if file size is larger than 0.
-r file	:	true if file is readable.
-w file	:	true if file is writable.
-x file	:	true if file is executable.

file1 -nt file2	:	true if file1 is newer than file2 in mtime.
file1 -ef file2	:	true if file1 and file2 refer to the same device and inode numbers.

-z string	:	true if length of string is 0.
string		:	true if length of string is non-zero.
-n string	:	true if length of string is non-zero.
str1 == str2	:	true if the strings are equal.

This will return true only if a variable is set.

# The following two are equal.
[ ! -z "$VAR" ];
[ -n "$VAR" ];

Check whether user exists

if id -u $1 >/dev/null 2>&1; then
	echo "user exists"
	echo "user does not exist"


Proxy setting

A good reference:

# wcfNote: Ruby rails prefers lower-case http_proxy etc. See
function proxyOn() {
    export http_proxy=myproxy:8080
    export https_proxy=$http_proxy
    export ftp_proxy=$http_proxy
    export socks_proxy=$http_proxy
    export no_proxy="localhost,,$USERDNSDOMAIN"
    export ARCHFLAGS="-arch x86_64"

    # Update git and npm to use the proxy
    git config http.proxy $http_proxy
    # git config http.sslcainfo /bin/curl-ca-bundle.crt
    # git config --global http.sslcainfo /bin/curl-ca-bundle.crt

    # optional for debugging
    export GIT_CURL_VERBOSE=1
    # optional Self Signed SSL certs and internal CA certificate in an corporate environment
    export GIT_SSL_NO_VERIFY=1

    # env | grep -e _PROXY -e GIT_ | sort
    # echo -e "\nProxy-related environment variables set."
export -f proxyOn

function proxyOff() {
    variables=( \
       "http_proxy" "https_proxy" "ftp_proxy" "socks_proxy" \
       "no_proxy" "GIT_CURL_VERBOSE" "GIT_SSL_NO_VERIFY" \
    for i in "${variables[@]}"
       unset $i
    env | grep -e _proxy -e GIT_ | sort
    echo -e "\nProxy-related environment variables removed."
export -f proxyOff

Segmentation fault

SO: docker command leads to segmentation fault.

It turns out to be that, I defined a "docker" function unintentionally in bash script, and called the real docker function, which caused endless recursion.

I would like to suggest to name all private functions with double underscores as prefix. See So: is there a convention for naming private functions in bash.

What is Login shell

SO: difference between login shell and non-login shell.

The login process tells the shell to behave as a login shell by prepending '-' before the shell's name, e.g. "-bash". So in a login shell, argv[0][0] == '-'.

echo $0
# On MacOS: -csh

A login shell can execute logout command. It is same as exit.

When you log in on a text console, or through SSH, or with "su -", you get an interactive login shell.

When you start a shell in a terminal in an existing session (screen, X terminal, Emacs terminal buffer, a shell inside another, etc.), you get an interactive, non-login shell.

When a shell runs a script or a command passed on its command line, it's a non-interactive, non-login shell.

The difference between login shells and non-login ones is in the files they load at initial time. For login-shells:

Bourne shell: /etc/profile and ~/.profile
bash: ~/.bash_profile
zsh: /etc/zprofile and ~/.zprofile
csh: /etc/csh.login and ~/.login

See below for more cases.

Bashrc not loaded automatically

After ssh login, bashrc also not loaded

wcfNote: I just set source ~/.bashrc in .bash_profile. And it works!

Run as another user


su wangcf <<'EOF'

pushd .
cd $cur
nohup python build/lib/ss/ &> ~/ss.log &

Multiline string


## Here we must use quotes, otherwise the linebreaks will be gone.
echo ""
# hehe
# haha
## The linebreaks are gone...
# hehe haha

Dash as positional parameter

Note that in this context the "-" is not itself a Bash operator, but rather an option recognized by certain UNIX utilities that write to stdout, such as tar, cat, etc.

Where a filename is expected, - redirects output to stdout (sometimes seen with tar cf), or accepts input from stdin, rather than from a file. This is a method of using a file-oriented utility as a filter in a pipe.

file -
# Input: abc
# Input: Ctrl+D
# Output: standard input:  ASCII text
file -
# Input: #!/bin/bash
# Input: Ctrl+D
# Output: standard input:  Bourne-Again shell script text executable

echo "whatever" | cat -
nc -l -p 12345 | tar xvzf -

Generate random strings and numbers

# Generate a random number with 6 digits.
< /dev/urandom tr -dc 0-9 | head -c6 && echo

SSH and run synclient, we get "Failed to connect to X Server".

LinuxQuestions. For synclient to work, it must use some environment variables set by X. The author grabbed the variables using export -p, and found they are XAUTHORITY and DISPLAY. So set those two vars:

declare -x DISPLAY=":0"
declare -x XAUTHORITY="/home/bob/.Xauthority"

if [ $1 = "on" ]
  synclient TouchpadOff=0

if [ $1 = "off" ]
  synclient TouchpadOff=1

BASH terminal has unexpected line breaks



# It is mostly to do with the size of the window assumed by the terminal is not the same as your actual window size. If you are using bash, you can try this.
shopt  | grep checkwinsize
# If you don't get
# checkwinsize    on
# Then activate it with
shopt -s checkwinsize 7

The issue is often caused by misconfiguring ~/.bashrc not to call /etc/bashrc. Normally, bash loads ~/.bashrc which is expected to call /etc/bashrc, which by default contains shopt -s checkwinsize.

Culprit 1

ArchLinuxBBS. It may be caused by your PS1 setting. Bash thinks you wrap non-printing characters in '\[' and '\]'. So avoid [ and ] in PS1.

Culprit 2


This issue happens generally when dumping binary data to the terminal "STDOUT" which when the escape codes received are processed can do anything from change the color of the text, disable echo, even change character set.

# Use this to reset all problematic bash


# -E: --extended-regexp
# [:space:] stands for a space. And it must reside in another [], thus [[:space:]].
echo "No , come on." | grep -iE 'error|No[[:space:]]'

# -o, --only-matching: Prints only the matching part of the lines. E.g. used to count matched line numbers.
grep -o "pattern" | wc -l

# Add the following to a bash script:
grep --color=always -riI --exclude-dir log --exclude *.log --exclude TAGS --exclude-dir bin --exclude-dir public --exclude-dir tmp "$1" .

Bash script not recognizes aliases

StackExchange. look into the bash manpage you find:

Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt.

# If not running interactively, don't do anything
[ -z "$PS1" ] && return
shopt -s expand_aliases
source ~/.bash_aliases

Bash to parse config


    database_user    = user
    database_version = 20110611142248

version=$(awk -F "=" '/database_version/ {print $2}' parameters.ini)

Dir name of script

## One-liner which will give you the full directory name of the script no matter where it is being called from.
# Take /a/b/ for example, $BASH_SOURCE[0] is "/a/b/".
# "dirname /a/b/" gives "/a/b".
# dirname only returns parent dir: "dirname /a/b/" returns "/a"
cur=$( cd "$( dirname "" )" && pwd )

## Suppress pushd output.
pushd () {
    command pushd "[email protected]" > /dev/null
popd () {
    command popd "[email protected]" > /dev/null

Detect OS in bash


###### OSTYPE var.

case $OSTYPE in darwin*) echo I am a Mac ;; esac

if [[ "$OSTYPE" == "linux-gnu" ]]; then
        # ...
elif [[ "$OSTYPE" == "darwin"* ]]; then
        # Mac OSX
elif [[ "$OSTYPE" == "cygwin" ]]; then
        # POSIX compatibility layer and Linux environment emulation for Windows
elif [[ "$OSTYPE" == "msys" ]]; then
        # Lightweight shell and GNU utilities compiled for Windows (part of MinGW)
elif [[ "$OSTYPE" == "win32" ]]; then
        # I'm not sure this can happen.
elif [[ "$OSTYPE" == "freebsd"* ]]; then
        # ...
        # Unknown.

### uname

if [[ "$unamestr" == 'Linux' ]]; then
elif [[ "$unamestr" == 'FreeBSD' ]]; then

if [[ $platform == 'linux' ]]; then
   alias ls='ls --color=auto'
elif [[ $platform == 'freebsd' ]]; then
   alias ls='ls -G'

-- dash

Show all directories


for d in */ ; do
    echo "$d"

Clear all env variables

One quick and clean solution: env -i /path/to/


unset $(/usr/bin/env | egrep '^(\w+)=(.*)$' | \
  egrep -vw 'PWD|USER|LANG' | /usr/bin/cut -d= -f1);

Read user input


## Equivalent to dos Pause
# -n1: wait for only one key.
# -r: raw mode, which don't allow combined characters like "\" or "^".
# -p: specifies the prompt.
# -p $'prompt': let spaces and escaped characters, rather than simple quote.
# -s: specifies silent mode, and because we don't need keyboard output.
read -n1 -rsp $"Press any key to continue...\n" key

read -sp "Password please: " pass
# Alternatively. Because the new line from Enter is hidden too, you've to print a newline.
stty -echo
read -p "Password please: " pass
stty echo
printf '\n'

# -t5: wait for 5 seconds.
read -t5 -n1 -r -p 'Press any key in the next five seconds...' key
if [ "$?" -eq "0" ]; then
	echo 'A key was pressed.'
	echo 'No key was pressed.'

# -d $'\e' specifies escappe as delimiter charater
# -e specifies readline mode.
# -i $'Y' specifies Y as initial text in readline mode.

Execute command as somebody

su somebody <<'EOF'
command1 -p 'parameter with "quotes" inline'
command2 -p 'parameter with "quotes" inline'
command3 -p 'parameter with "quotes" inline'

sudo -u username command
If you encounter "su: must be run from a terminal", try sudo su somebody <<'EOF'.
If you use <<EOF instead of <<'EOF' then variables inside the here-doc will be expanded.

String operation


String length

Three ways:

expr length $str
expr "$str":'.*'
The first two are equivalent of strlen() in C.


echo ${#stringZ}                 # 15
echo `expr length $stringZ`      # 15
echo `expr "$stringZ" : '.*'`    # 15

Length of matching substring

expr match "$string" '$substring' $substring is a regular expression. expr "$string" : '$substring' $substring is a regular expression.

#       |------|
#       12345678

echo `expr match "$stringZ" 'abc[A-Z]*.2'`   # 8
echo `expr "$stringZ" : 'abc[A-Z]*.2'`       # 8

Substring Extraction

${string:position}	Extracts substring from $string at $position.
	Extracts $length characters of substring from $string at $position.

#       0123456789.....
#       0-based indexing.

echo ${stringZ:0}                            # abcABC123ABCabc
echo ${stringZ:1}                            # bcABC123ABCabc
echo ${stringZ:7}                            # 23ABCabc
echo ${stringZ:7:3}                          # 23A

# Is it possible to index from the right end of the string?
echo ${stringZ:-4}                           # abcABC123ABCabc
echo ${stringZ:-}                            # abcABC123ABCabc
# Defaults to full string, as in ${parameter:-default}.
# However . . .
echo ${stringZ:(-4)}                         # Cabc 
echo ${stringZ: -4}                          # Cabc
# Now, it works.
# Parentheses or added space "escape" the position parameter.

Generating an 8-character "random" string

if [ -n "$1" ]  #  If command-line argument present,
then            #+ then set start-string to it.
else            #  Else use PID of script as start-string.

POS=2  # Starting from position 2 in the string.
LEN=8  # Extract eight characters.

str1=$( echo "$str0" | md5sum | md5sum )
#  Doubly scramble     ^^^^^^   ^^^^^^
#+ by piping and repiping to md5sum.

# Can parameterize ^^^^ ^^^^

echo "$randstring"
exit $?

# ./ my-password
# 1bdd88c4

Substring Removal

Deletes shortest match of $substring from front of $string.
Deletes longest match of $substring from front of $string.

Deletes shortest match of $substring from back of $string.
Deletes longest match of $substring from back of $string.

#       |----|          shortest
#       |----------|    longest

echo ${stringZ#a*C}      # 123ABCabc
# Strip out shortest match between 'a' and 'C'.

echo ${stringZ##a*C}     # abc
# Strip out longest match between 'a' and 'C'.

# You can parameterize the substrings.
echo ${stringZ#$X}      # 123ABCabc
echo ${stringZ##$X}     # abc

#                    ||     shortest
#        |------------|     longest

echo ${stringZ%b*c}      # abcABC123ABCa
# Strip out shortest match between 'b' and 'c', from back of $stringZ.

echo ${stringZ%%b*c}     # a
# Strip out longest match between 'b' and 'c', from back of $stringZ.

# Rename all filenames in $PWD with "TXT" suffix to a "txt" suffix.
# For example, "file1.TXT" becomes "file1.txt" . . .


for i in $(ls *.$SUFF)
  mv -f $i ${i%.$SUFF}.$suff
  #  Leave unchanged everything *except* the shortest pattern match
  #+ starting from the right-hand-side of the variable $i . . .
done ### This could be condensed into a "one-liner" if desired.

Substring Replacement


# Replaces first match of 'abc' with 'xyz'.
echo ${stringZ/abc/xyz}	# xyzABC123ABCabc
# Replaces all matches of 'abc' with # 'xyz'.
echo ${stringZ//abc/xyz}	# xyzABC123ABCxyz
# The string itself is not altered!
echo "$stringZ"               # abcABC123ABCabc

# Can the match and replacement strings be parameterized?
echo ${stringZ/$match/$repl}  # 000ABC123ABCabc
#              ^      ^         ^^^
echo ${stringZ//$match/$repl} # 000ABC123ABC000
# Yes!          ^      ^        ^^^         ^^^

# A simple deletion takes place if no $replacement string is supplied.
echo ${stringZ/abc}           # ABC123ABCabc
echo ${stringZ//abc}          # ABC123ABC

# Replaces front-end match of 'abc' with 'XYZ'.
echo ${stringZ/#abc/XYZ}          # XYZABC123ABCabc
# Replaces back-end match of 'abc' with 'XYZ'.
echo ${stringZ/%abc/XYZ}          # abcABC123ABCXYZ


Only capitalize the first char:

A="asdf asdf asdf";
for i in $A; do
  B=`echo "${i:0:1}" | tr a-z A-Z`${i:1};
  echo -n "$B ";
# Asdf Asdf Asdf