Bash Shell 速查表 Cheat Sheet

入门指南

hello.sh

#!/bin/bash

VAR="world"
echo "Hello $VAR!" # => Hello world!

执行脚本

bash hello.sh

变量

NAME="John"

echo ${NAME}    # => John (变量)
echo $NAME      # => John (变量)
echo "$NAME"    # => John (变量)
echo '$NAME'    # => $NAME (精确字符串)
echo "${NAME}!" # => John! (变量)

NAME = "John"   # => 错误 (关于空格)

注释

# 这是一个Bash内联注释。
: '
这是一个
非常整洁的
Bash注释
'

多行注释使用 :' 开始,' 结束

参数

表达式 描述
$1$9 参数1 … 9
$0 脚本本身的名字
$1 第一个参数
${10} 位置参数10
$# 参数数量
$$ Shell的进程ID
$* 所有参数
$@ 所有参数,从第一个开始
$- 当前选项
$_ 上一个命令的最后一个参数

参见: 特殊参数

函数

get_name() {
    
    
    echo "John"
}

echo "You are $(get_name)"

参见: 函数

条件判断

if [[ -z "$string" ]]; then
    echo "字符串为空"
elif [[ -n "$string" ]]; then
    echo "字符串不为空"
fi

参见: 条件判断

花括号扩展

echo {
    
    A,B}.js
表达式 描述
{A,B} 等同于 A B
{A,B}.js 等同于 A.js B.js
{1..5} 等同于 1 2 3 4 5

参见: 花括号扩展

Shell执行

# => 我在 /path/of/current
echo "我在 $(PWD)"

# 相同于:
echo "我在 `pwd`"

参见: 命令替换

Bash参数扩展

语法

代码 描述
${FOO%suffix} 删除后缀
${FOO#prefix} 删除前缀
${FOO%%suffix} 删除长后缀
${FOO##prefix} 删除长前缀
${FOO/from/to} 替换第一个匹配
${FOO//from/to} 替换所有匹配
${FOO/%from/to} 替换后缀
${FOO/#from/to} 替换前缀
子字符串
表达式 描述
${FOO:0:3} 子字符串 (位置,长度)
${FOO:(-3):3} 从右侧开始的子字符串
长度
表达式 描述
${#FOO} $FOO 的长度
默认值
表达式 描述
${FOO:-val} $FOO,或如果未设置则为 val
${FOO:=val} 如果未设置则将 $FOO 设为 val
${FOO:+val} 如果 $FOO 已设置则为 val
${FOO:?message} 如果未设置 $FOO 则显示消息并退出

替换

echo ${food:-Cake}  #=> $food 或 "Cake"
STR="/path/to/foo.cpp"
echo ${STR%.cpp}    # /path/to/foo
echo ${STR%.cpp}.o  # /path/to/foo.o
echo ${STR%/*}      # /path/to

echo ${STR##*.}     # cpp (扩展名)
echo ${STR##*/}     # foo.cpp (基名)

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

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

切片

name="John"
echo ${name}           # => John
echo ${name:0:2}       # => Jo
echo ${name::2}        # => Jo
echo ${name::-1}       # => Joh
echo ${name:(-1)}      # => n
echo ${name:(-2)}      # => hn
echo ${name:(-2):2}    # => hn

length=2
echo ${name:0:length}  # => Jo

参见: 参数扩展

基路径 & 目录路径

SRC="/path/to/foo.cpp"
BASEPATH=${SRC##*/}
echo $BASEPATH  # => "foo.cpp"


DIRPATH=${SRC%$BASEPATH}
echo $DIRPATH   # => "/path/to/"

转换

STR="HELLO WORLD!"
echo ${STR,}   # => hELLO WORLD!
echo ${STR,,}  # => hello world!

STR="hello world!"
echo ${STR^}   # => Hello world!
echo ${STR^^}  # => HELLO WORLD!

ARR=(hello World)
echo "${ARR[@],}" # => hello world
echo "${ARR[@]^}" # => Hello World

Bash数组

定义数组

Fruits=('Apple' 'Banana' 'Orange')

Fruits[0]="Apple"
Fruits[1]="Banana"
Fruits[2]="Orange"

ARRAY1=(foo{
    
    1..2}) # => foo1 foo2
ARRAY2=({
    
    A..D})    # => A B C D

# 合并 => foo1 foo2 A B C D
ARRAY3=(${ARRAY1[@]} ${ARRAY2[@]})

# declare 语法
declare -a Numbers=(1 2 3)
Numbers+=(4 5) # 追加 => 1 2 3 4 5

索引

- -
${Fruits[0]} 第一个元素
${Fruits[-1]} 最后一个元素
${Fruits[*]} 所有元素
${Fruits[@]} 所有元素
${#Fruits[@]} 所有元素数量
${#Fruits} 第一个元素的长度
${#Fruits[3]} 第n个元素的长度
${Fruits[@]:3:2} 范围
${!Fruits[@]} 所有的键

迭代

Fruits=('Apple' 'Banana' 'Orange')

for e in "${Fruits[@]}"; do
    echo $e
done
带索引
for i in "${
     
     !Fruits[@]}"; do
  printf "%s\t%s\n" "$i" "${Fruits[$i]}"
done

操作

Fruits=("${Fruits[@]}" "Watermelon")     # 添加
Fruits+=('Watermelon')                   # 也是添加
Fruits=( ${Fruits[@]/Ap*/} )             # 通过正则匹配删除
unset Fruits[2]                          # 删除一个元素
Fruits=("${Fruits[@]}")                  # 复制
Fruits=("${Fruits[@]}" "${Veggies[@]}")  # 连接
lines=(`cat "logfile"`)                  # 从文件读取

数组作为参数

function extract()
{
    
    
    local -n myarray=$1
    local idx=$2
    echo "${myarray[$idx]}"
}
Fruits=('Apple' 'Banana' 'Orange')
extract Fruits 2     # => Orangle

Bash字典

定义

declare -A sounds


sounds[dog]="bark"
sounds[cow]="moo"
sounds[bird]="tweet"
sounds[wolf]="howl"

操作字典

echo ${sounds[dog]} # 狗的声音
echo ${sounds[@]}   # 所有值
echo ${
    
    !sounds[@]}  # 所有键
echo ${
    
    #sounds[@]}  # 元素数量
unset sounds[dog]   # 删除dog

迭代

for val in "${sounds[@]}"; do
    echo $val
done

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

Bash条件判断

整数条件

条件 描述
[[ NUM -eq NUM ]] 相等
[[ NUM -ne NUM ]] 不等
[[ NUM -lt NUM ]] 小于
[[ NUM -le NUM ]] 小于等于
[[ NUM -gt NUM ]] 大于
[[ NUM -ge NUM ]] 大于等于
(( NUM < NUM )) 小于
(( NUM <= NUM )) 小于等于
(( NUM > NUM )) 大于
(( NUM >= NUM )) 大于等于

字符串条件

条件 描述
[[ -z STR ]] 空字符串
[[ -n STR ]] 非空字符串
[[ STR == STR ]] 相等
[[ STR = STR ]] 相等 (同上)
[[ STR < STR ]] 小于 (ASCII)
[[ STR > STR ]] 大于 (ASCII)
[[ STR != STR ]] 不相等
[[ STR =~ STR ]] 正则匹配

示例

字符串
if [[ -z "$string" ]]; then
    echo "字符串为空"
elif [[ -n "$string" ]]; then
    echo "字符串不为空"
else
    echo "这永远不会发生"
fi
组合
if [[ X && Y ]]; then
    ...
fi
相等
if [[ "$A" == "$B" ]]; then
    ...
fi
正则表达式
if [[ '1. abc' =~ ([a-z]+) ]]; then
    echo ${
    
    BASH_REMATCH[1]}
fi
小于
if (( $a < $b )); then
   echo "$a 小于 $b"
fi
存在
if [[ -e "file.txt" ]]; then
    echo "文件存在"
fi

文件条件

条件 描述
[[ -e FILE ]] 存在
[[ -d FILE ]] 目录
[[ -f FILE ]] 文件
[[ -h FILE ]] 符号链接
[[ -s FILE ]] 大小大于0字节
[[ -r FILE ]] 可读
[[ -w FILE ]] 可写
[[ -x FILE ]] 可执行
[[ f1 -nt f2 ]] f1 比 f2 新
[[ f1 -ot f2 ]] f2 比 f1 旧
[[ f1 -ef f2 ]] 相同文件

更多条件

条件 描述
[[ -o noclobber ]] 如果选项已启用
[[ ! EXPR ]] 不是
[[ X && Y ]] 并且
`[[ X

逻辑和,或

if [ "$1" = 'y' -a $2 -gt 0 ]; then
    echo "是"
fi

if [ "$1" = 'n' -o $2 -lt 0 ]; then
    echo "否"
fi

Bash循环

基本的for循环

for i in /etc/rc.*; do
    echo $i
done

类C的for循环

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

范围

for i in {
    
    1..5}; do
    echo "欢迎 $i"
done
带步长
for i in {
    
    5..50..5}; do
    echo "欢迎 $i"
done

自动增量

i=1
while [[ $i -lt 4 ]]; do
    echo "数字: $i"
    ((i++))
done

自动减量

i=3
while [[ $i -gt 0 ]]; do
    echo "数字: $i"
    ((i--))
done

continue

for number in $(seq 1 3); do
    if [[ $number == 2 ]]; then
        continue;
    fi
    echo "$number"
done

break

for number in $(seq 1 3); do
    if [[ $number == 2 ]]; then
        # 跳过整个循环的剩余部分。
        break;
    fi
    # 这只会打印1
    echo "$number"
done

until

count=0
until [ $count -gt 10 ]; do
    echo "$count"
    ((count++))
done

死循环

while true; do
    # 这里有一些代码。
done

死循环 (简写)

while :; do
    # 这里有一些代码。
done

读取行

cat file.txt | while read line; do
    echo $line
done

Bash函数

定义函数

myfunc() {
    
    
    echo "你好 $1"
}
# 同上(另一种语法)
function myfunc() {
    
    
    echo "你好 $1"
}
myfunc "John"

返回值

myfunc() {
    
    
    local myresult='某个值'
    echo $myresult
}
result="$(myfunc)"

抛出错误

myfunc() {
    
    
    return 1
}
if myfunc; then
    echo "成功"
else
    echo "失败"
fi

Bash选项

选项

# 避免覆盖文件
# (echo "hi" > foo)
set -o noclobber

# 用于在出错时退出
# 避免级联错误
set -o errexit

# 揭示隐藏的失败
set -o pipefail

# 暴露未设置的变量
set -o nounset

通配选项

# 不匹配的通配符将被删除
# ('*.foo' => '')
shopt -s nullglob

# 不匹配的通配符将抛出错误
shopt -s failglob

# 不区分大小写的通配符
shopt -s nocaseglob

# 通配符匹配点文件
# ("*.sh" => ".foo.sh")
shopt -s dotglob

# 允许 ** 进行递归匹配
# ('lib/**/*.rb' => 'lib/a/b/c.rb')
shopt -s globstar

Bash历史

命令

命令 描述
history 显示历史
sudo !! 使用sudo执行上一个命令
shopt -s histverify 不立即执行扩展结果

扩展

表达式 描述
!$ 扩展最近命令的最后一个参数
!* 扩展最近命令的所有参数
!-n 扩展最近第 n 个命令
!n 扩展历史中的第 n 个命令
!<command> 扩展最近一次调用 <command> 的命令

操作

代码 描述
!! 再次执行上一个命令
!!:s/<FROM>/<TO>/ 将最近命令中的第一个 <FROM> 替换为 <TO>
!!:gs/<FROM>/<TO>/ 将最近命令中的所有 <FROM> 替换为 <TO>
!$:t 仅扩展最近命令的最后一个参数的基名
!$:h 仅扩展最近命令的最后一个参数的目录名

!!!$ 可以替换为任何有效的扩展。

切片

代码 描述
!!:n 仅扩展最近命令的第 n 个参数
!^ 扩展最近命令的第一个参数
!$ 扩展最近命令的最后一个参数
!!:n-m 扩展最近命令的第 n 个到第 m 个参数
!!:n-$ 扩展最近命令的第 n 个参数到最后一个参数

!! 可以替换为任何有效的扩展,例如 !cat!-2!42 等。

其他

数值计算

$((a + 200))      # 给 $a 加200
$(($RANDOM%200))  # 随机数0..199

子Shell

(cd somedir; echo "我现在在 $PWD")
pwd # 仍在第一个目录

检查命令

command -V cd
#=> "cd 是一个函数/别名/其他"

重定向

python hello.py > output.txt   # 标准输出到(文件)
python hello.py >> output.txt  # 标准输出到(文件),追加
python hello.py 2> error.log   # 标准错误到(文件)
python hello.py 2>&1           # 标准错误到标准输出
python hello.py 2>/dev/null    # 标准错误到(空设备)
python hello.py &>/dev/null    # 标准输出和标准错误到(空设备)
python hello.py < foo.txt      # 将 foo.txt 输入到 python

相对来源

source "${0%/*}/../share/foo.sh"

脚本目录

DIR="${0%/*}"

案例/开关

case "$1" in
    start | up)
    vagrant up
    ;;

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

捕获错误

trap 'echo 错误发生在 $LINENO' ERR

或者

traperr() {
    
    
    echo "错误: ${
     
     BASH_SOURCE[1]} 发生在 ${
     
     BASH_LINENO[0]}"
}

set -o errtrace
trap traperr ERR

printf

printf "Hello %s, I'm %s" Sven Olga
#=> "Hello Sven, I'm Olga

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

printf "打印一个浮点数: %f" 2
#=> "打印一个浮点数: 2.000000"

获取选项

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

检查命令结果

if ping -c 1 google.com; then
    echo "看来你有一个工作正常的互联网连接"
fi

特殊变量

表达式 描述
$? 上一个任务的退出状态
$! 上一个后台任务的PID
$$ Shell的PID
$0 Shell脚本的文件名

参见 特殊参数.

Grep检查

if grep -q 'foo' ~/.bash_history; then
    echo "你似乎在过去曾经输入过 'foo'"
fi

反斜杠转义

  • &nbsp;
  • \!
  • \"
  • \#
  • \&
  • \'
  • \(
  • \)
  • \,
  • \;
  • \<
  • \>
  • \[
  • \|
  • \\
  • \]
  • \^
  • \{
  • \}
  • ```
  • \$
  • \*
  • \?

使用 \ 转义这些特殊字符

Heredoc

cat <<END
hello world
END

返回上一个目录

pwd # /home/user/foo
cd bar/
pwd # /home/user/foo/bar
cd -
pwd # /home/user/foo

读取输入

echo -n "继续? [y/n]: "
read ans
echo $ans
read -n 1 ans    # 只读一个字符

条件执行

git commit && git push
git commit || echo "提交失败"

严格模式

set -euo pipefail
IFS=$'\n\t'

参见: 非官方的bash严格模式

可选参数

args=("$@")
args+=(foo)
args+=(bar)
echo "${args[@]}"

将参数放入数组然后追加

另见

猜你喜欢

转载自blog.csdn.net/Leon_Chenl/article/details/140341323