Bash tips

2.1

Key bindings

  • CTRL+F to move forward by a char
  • CTRL+B to move backward by a char
  • CTRL+A to jump to start of the line
  • CTRL+E to jump to end of the line
  • CTRL+K to kill the line starting from the cursor position
  • ALT+D to delete a word starting from the current cursor position
  • CTRL+W to remove the word backwards from cursor position
  • CTRL+Y to paste text from the kill buffer
  • CTRL+R to reverse search for commands you typed in the past from your history.
  • CTRL+S to forward search (works in ZSH for me but not bash)- CTRL+F to move forward by a char

Case sensitive

  • bash like the rest of UNIX is case sensitive

  • MacOS & Windows filesystem is case insensitive

$SECONDS Time the script/shell has been running for

# use uppercase
echo $SECONDS

logical operators

&&, || can be used inside or outside double bracket [[

if [[ ($A -eq 0 ]] && [[ $C == "abc" ]]; thenif [[ ($A -eq 0 || $B -ne 0) && $C == "abc" ]]; then

$... substitutions

$(), $(()), ${} usually return value of the commands inside

bracket [, [[, parentheses (), (()), braces {}

  • [ single bracket calls program test, see man test

    var=abcdef
    if [ $var == abcdef ]; then
      echo yes
    else
      echo no
    fi
    
  • [[ double bracket does the same thing (basically) as a single bracket [, but is a bash builtin. Particularly, it supports =~ for regular expression

    if [[ $var == abcdef ]]; then
    if [[ $var =~ [a-z] ]]; then
    
  • () parentheses create a subshell and run the commands

    pwd
    (cd /tmp; pwd)
    pwd
    
  • $() returns the result

    var=$(cd /tmp; pwd)
    
  • (()), $(()) arithmetic context to calculate numbers

    if (( a > b))
    
    i = $((j + 3))
    
    
  • {} groups commands and return concated output

    {date; top -b -n1 | head;} > logfile
    
  • ${} returns value of the variable named in the braces

    ${var}_name
    
    # this is same as ${var_name}
    $var_name 
    

[ can be used in plain sh. [[ and (( are specific to bash (and ksh and zsh)

https://stackoverflow.com/questions/31255699/double-parenthesis-with-and-without-dollar

Sleep

sleep N pause script for N seconds

Remove all except for one file

https://unix.stackexchange.com/questions/153862/remove-all-files-directories-except-for-one-file

ls | grep -xv "file.txt" | xargs rm
ls | grep -xv "file.txt" | parallel rm

from man grep:

-v, --invert-match
         Invert the sense of matching, to select non-matching lines.  (-v is specified by POSIX)


-x, --line-regexp
         Select  only  those  matches  that exactly match the
         whole line.  For a regular expression pattern,  this
         is   like   parenthesizing   the  pattern  and  then
         surrounding it with ^ and $.

Shell Style Guide

https://google.github.io/styleguide/shellguide.html

变量相加

$((a + b))

数组长度

${#array}

String replace

  • ${string/pattern/new} replace
  • ${string//pattern/new} replace all
message='The secret code is 12345'
echo ${message/[0-9]/X}
# The secret code is X2345

echo ${message//[0-9]/X}
# The secret code is XXXXX

获取文件列表

files=$(ls /path/to/*.txt)
echo $files

for f in $files
do
  echo $f
done

Single quotes / double quotes

Single quotes won't interpolate anything, but double quotes will. For example, variables, expressions, backticks, certain \ escapes, etc.

Example

echo "$(echo "upg")"
# upg

echo '$(echo "upg")'
# $(echo "upg")

difference-between-single-and-double-quotes-in-bash

function

# $1, $2, ... $n, ..., $#
greeting() {
  echo "Hello $1"
}

greeting "Joe"

result="$(greeting 'Joe')"

bash functions

Check if a file or directory exists

使用条件判断和 -f 参数或 -d 参数

test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
FILE=/ect/resolve.conf
if [ -f "$FILE" ]; then
    echo "$FILE exists."
else
    echo "$FILE does not exist"
fi
FILE=/etc/docker
if [ -d "$FILE" ]; then
    echo "$FILE is a directory"
fi

How to Check if a File or Directory Exists in Bash | Linuxize

判断字符串为空(string is null or empty)

status=`git status | grep commit`

if [ ! -z "$status" ]; then
    echo 'is not empty'
else
    echo 'is empty'
fi

比较字符串 Compare string

  • [ string1 = string2 ]: equal in []

  • [[ string1 == string2 ]]: equal in [[]]

  • string1 != string2: not equal

  • string1 =~ regex: regex match

  • -z string1: zero length

  • -n string1: not zero length

a="Linux"
b="macos"

if [[ $a == $b ]]; then
    echo "Strings are equal."
else
    echo "Strings are not equal."
fi

if [[ $a =~ .*nux.* ]]; then
    echo "It's there"
fi

for 循环

projects=(p1 p2)
for i in "${projects[@]}"; do
    echo $i '-'
done

Local variable / environment variable

# Only in current shell
name=demo

# Can be accessed by process.env.NODE_ENV in script
export NODE_ENV=production

${}, $()

  • ${var}: 获取并代入变量的值

  • $(command): 执行命令并返回结果

  • `command`: 执行命令并返回结果

Bash function

所有变量(包括函数内)默认是全局变量,除非在函数内用local声明。

Bash Functions | Linuxize

#!/bin/bash

greeting () {
  echo "Hello $1"
}

greeting "Joe"

cd to the path of the script

cd "$(dirname "$0")"

获取 bash 脚本的 arguments

bash 函数通过这种方式拿到的是本身的参数。

copy.sh -from a.file -to b.file

$# # 4, total number

$0 # copy.sh, script file name

$1 # -from
$2 # a.file
$3 # -to
$4 # b.file

检查是否传递了参数

# 没有传递任何参数
if [ $# -eq 0 ]
    then
        echo "No arguments supplied"
fi

# 没有传递第一个参数
if [ -z "$1" ]
    then
        echo "No argument supplied"
fi

bash: Get OS type

if [[ "$OSTYPE" == "linux-gnu" ]]; then
    # ...
elif [[ "$OSTYPE" == "darwin"* ]]; then
        # ...
fi

os detection - How to detect the OS from a Bash script? - Stack Overflow

zsh tips

You have new mail.

mail

type number ID to read; d 1-9 to delete; x/q to leave.

cd to history

cd -
cd -2
cd -3

cd -<tab>

Bash 批量文件重命名

Rename files in linux

  • Single file

    mv [option] source destination
    
    mv file1.txt file2.txt
    
  • Multiple files with sed regexp

    files="*.wav"
    for f in $files; do
      newname=`echo "$f" | sed 's/[0-9]*//g'`
      mv "$f" "$newname"
    done
    
  • Multiple files

    for f in *.html; do
        mv "$f" "${f%.html}.pug"
    done
    

    ${f%.html} is using the Shell parameter expansion.

    ${f%.html} 匹配末尾, ${f#../ouptput} 匹配开头

  • Multiple files with find

    find . -type f -name "*.html" -exec sh -c 'f="{}"; mv -- "$f" "${f%.html}.pug"' \;
    

Bash 支持换行符

printf 更通用

    printf "hello\nworld"

echo 在不同环境不同版本可能不一致

    echo 'Hello\nworld'
    echo -e "Hello\nworld"
    echo $'Hello\nworld'

出错立即退出脚本

参考

默认情况下 shell 一条命令出错后仍会执行下面的命令,可以通过设置,在出错后立即退出脚本

    set -e

    # commands which fail will cause the script to exit immediately

    bash -e my_script.sh
📖