References

Shebang

#!/bin/bash

# 在 shebang 行中使用 env 命令
#!/usr/bin/env python

Variables

# 定义变量
var="a string" #等号两边不能有空格

#调用变量
echo $var
echo ${var}

# `""` suppress expansions except parameter expansion, arithmetic expansion, and command substitution
echo "$var" # 双引号会渲染变量,输出`a string`

# `''` suppress all expansions
echo '$var' # 单引号不会渲染变量,输出`$var`

# Arithmetic Expansion
echo $((1+1)) # 输出`2`
echo $((1.0/2)) # 输出`0.5`

# Command Substitution
# 执行命令并将输出替换原来内容
`cat some.txt`
$( cat some.txt )

# Process Substitution
# 执行命令并将输出写到一个临时文件中,并用临时文件名替换原来内容
# 在我们希望返回值通过文件而不是STDIN传递时很有用
diff <(ls foo) <(ls bar) #显示文件夹`foo`和`bar`中文件的区别

# Globbing 通配符
# `?`匹配一个字符,`*`匹配任意个字符
rm foo*

# Brace Expansion
# 花括号展开
echo Number_{1..5}
echo Front-{A,B,C}-Back

Positional Paramaters

  • $0:路径
    • dirname $0:路径名
    • basename $0:文件名
  • $1~$9:输入参数
  • $@: 所有参数,$*:区别是会展开双引号中的参数
  • $#: 参数个数
  • $?:上一次退出状态,0为正常,其他为错误
  • $$: 当前脚本的进程识别码

Logit

Expression Logit
[[ exp1 && exp2 ]] and
[[ exp1 || exp2 ]] or
[[ ! exp1 ]] not
# do command2 if command1 is sucessful
command1 && command2
# do command2 if command1 is unsucessful
command1 || command2

Function

function func {
    commands
    return
}

# or
func () {
    commands
    return
}

# local variable
function func {
    local var
    var=2
    return
}

# function arguments
func () {
   echo "Parameter #1 is $1" # arguments are refered by position
}
func "$arg1" "$arg2" # call function with arguments

Branching and Looping

If

x=5
if [[ $x -eq 5 ]]; then
    echo "x equals 5."
elif [[ $x -eq 4 ]]; then
    echo "x equals 4."
else
    echo "x does not equal 5 or 4."
fi

Case

read -p
case $REPLY in
    0)    echo "zero"
        ;;
    1)    echo "one"
        ;;
    2)    echo "two"
        ;;
    *)    echo "other"
        ;;
esac

While

count=1
while [[ $count -le 5 ]]; do 
    echo $count 
    count=$((count + 1)) 
done

while true; do
    echo 1
    sleep 1
done

For

for (( i=0; i<5; i=i+1 )); do 
    echo $i
done

for i in {1..100}; do
    echo $i
done

# iterate arguments from the 3rd
for i in "${@:3}"; do
	echo $i
done

Test

test 手册

尽量使用双方括号 [[ ]] 而不是单方括号 [ ],这样会降低犯错的几率,尽管这样并不能兼容 sh

File test

Expression True
[ -e file ] file exists
[ -d file ] file exists and is a directory
[ -f file ] file exists and is a regular file
[ file1 -ef file2 ] the two filenames refer to the same file by hard linking

String test

Expression True
[ string ] not null
[ -n string ] The length is greater than zero
[ -z string ] The length is zero
[ string1 = string2 ] equal
[ string1 != string2 ] not equal
[ string1 > string2 ] string1 sorts after string2
[[ string =~ regex ]] matched by the regular expression
[[ string == *.txt ]] matched by the expansion

Integer test

if ((INT < 0)); then
    echo "here"
elif (( ((INT % 2)) == 0)); then
    echo "here"
elif (( ((INT % 2)) == 0 && ((INT % 3)) == 0 )); then
    echo "here"
else
    echo "here"
fi

Command test

if ls /root; then
    echo success
else
    echo fail
fi

if ! grep $host $machines_file; then
    echo not found
fi

ls /nonexistent
if [ $? -ne 0 ]; then
  # command returned non-zero
else
  # command returned 0
fi

常用用法

多行输入

# here document
command <<- _EOF_
    text
_EOF_

读取输入

read a
echo "input is a=${a}"

导入其他变量

config.sh定义常量

const1=1
const2='a'

导入变量

source config.sh

脚本选项

# Option parsing
while [[ $# -gt 0 ]]; do
    key="$1"

    case $key in
        -q|--quiet)
            QUIET=true
            ;;
        -n|--filename)
            FILENAME="$2"
            shift
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            (>&2 printf "Unknown parameter: %s\n" "$1")
            usage
            exit 1
            ;;
    esac
    shift
done

if [ ! "$QUIET" ]; then
    echo "verbose"
fi

getops

#!/bin/bash

# Echo usage if something isn't right.
usage() { 
    echo "Usage: $0 [-p <80|443>] [-h <string>] [-f] <arg>" 1>&2; exit 1; 
}

while getopts ":p:h:fg" o; do
    case "${o}" in
        p)
            arg_port=${OPTARG}
            [[ $arg_port != "80" && $arg_port != "443" ]] && usage
            ;;
        h)
            arg_host=${OPTARG}
            ;;
        f)  
            flag_f=1
            ;;
        g)  
            flag_g=1
            ;;
        :)  
            echo "ERROR: Option -$OPTARG requires an argument"
            usage
            ;;
        \?)
            echo "ERROR: Invalid option -$OPTARG"
            usage
            ;;
    esac
done
shift $((OPTIND-1))

# Check required switches exist
if [ -z "${arg_port}" ] || [ -z "${arg_host}" ]; then
    usage
fi

if (( flag_f > 0 )); then
    echo "p = ${arg_port}"
    echo "h = ${arg_host}"
fi

if (( flag_g > 0 )); then
    for arg in "$@"; do
        args+=("$arg")
        echo $arg    
    done
fi
  • the first : denote if getopts should throw an error if parameters are missing

  • p: expects -p with an attribute

  • f with no colon expects -f with no attribute

  • shift $((OPTIND-1)) removes all the options that have been parsed by getopts

  • for arg in "$@" iterate the rest arguments

  • usage example: myscript.sh -p 443 -h myhost.com -f some_arg

调用 Python

curl -s "${URL}" | python -c 'import sys, json; print(json.load(sys.stdin)["images"][0]["url"])' > "$CACHE_DIR"/$(date +%Y-%m-%d).jpg