Bash介绍

介绍Bash

字符串操作

1
s=abcdefghijk

长度

1
2
# echo ${#s}
11

slice

1
2
3
4
5
6
7
# echo ${s:1:3}
bcd
# echo ${s:1:-1}
bcdefghij
# echo ${s:-1:-1}
abcdefghijk
# t=aaabbbccc

从头部的最长匹配,删除matching部分

1
2
# echo ${t##a*b}
ccc

从头部最短匹配

1
2
# echo ${t#a*b}
bbccc

失配

1
2
# echo ${t#m*m}
aaabbbccc

尾部匹配改成%

变量

  1. $?
    上一个指令退出码

  2. $$
    进程ID,等于BASHPID

  3. $_
    上一个命令的最后一个参数

  4. $!
    最近一个后台执行的异步命令(也就是后面加&的)的进程 ID

  5. $0
    shell的名称,或者运行的shell脚本的名称,大于等于10的用${10}等

  6. $#
    参数个数

  7. $@
    空格分割的全部参数
    可以循环 $@

    1
    2
    3
    for i in "$@"; do
    echo $i
    done
  8. $*
    $IFS分割的全部参数,貌似要echo "$*"才行。IFS是Internal Field Separator
    注意#@$*的行为在加引号之后不一样,"$*"会作为一个整体

  9. ~+
    pwd

  10. ~-
    OLDPWD的值

  11. ~user
    user用户的目录

注意区分makefile的参数$@扩展成当前规则的目的文件名,$<扩展成依赖列表中的第一个依赖文件,而$^扩展成整个依靠的列表(除掉了里面所有重复的文件名)。

下面是一个供展示的bash脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
echo $0
echo $1
echo $-
echo "SHARP"
echo $#
echo "AT"
echo "$@"
echo $@
export IFS=M
echo "STAR"
echo "$*"
echo $*

echo "For AT \""
for i in "$@"; do
echo $i
done

echo "For AT"
for i in $@; do
echo $i
done

echo "For STAR \""
for i in "$*"; do
echo $i
done

echo "For STAR"
for i in $*; do
echo $i
done

表达式

${}表达式

也称为Parameter expansions规则
${parameter:-word}:如果parameter是unset或者null,那么表达式的值是word的展开。
${parameter:=word}:返回值同上,但是parameter的值会被设置成word
${parameter:?word}:如果是unset或者null,则表示一个错误。错误可能被写到stderr,并且通过非0返回值返回。
${parameter:+word}:如果是unset或者null,则用null替换;否则用word替换。也就是不为空替换。

注意,我们需要区分$(command),这个表示用command的output进行替换,等同于反引号backquote的写法。

命令

source在当前sh执行脚本,简写是.。sh会创建一个新的。
--表示参数是传给后面的
例如cargo test -- --nocapture,则--nocapture并不是传给cargo test的,而是传给cargo test生成的二进制的。

控制

if后面可以跟任意数量的命令。这时,所有命令都会执行,但是判断真伪只看最后一个命令,即使前面所有命令都失败,只要最后一个命令返回0,就会执行then的部分。

1
2
3
4
5
$ if false; true; then echo 'hello world'; fi
hello world

$ if true; then echo 'hello world'; fi
hello world

if结构的判断条件,一般使用test命令,有三种形式。第三种形式还支持正则判断,前两种不支持。expression 为真,test命令执行成功(返回值为0);expression为伪,test命令执行失败(返回值为1)。

1
2
3
4
5
# type -a [[
[[ is a shell keyword
# type -a [
[ is a shell builtin
[ is /usr/bin/[
  1. 写法一

    1
    test expression
  2. 写法二
    注意,因为中括号实际上是一个命令,所以左右都要有空格

    1
    [ expression ]
  3. 写法三

    1
    [[ expression ]]

    这种写法还支持正则表达式正则表达式[[ expr =~ regex ]]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    MIN_VAL=1
    MAX_VAL=100

    INT=50

    if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
    if [[ $INT -ge $MIN_VAL && $INT -le $MAX_VAL ]]; then
    echo "$INT is within $MIN_VAL to $MAX_VAL."
    else
    echo "$INT is out of range."
    fi
    else
    echo "INT is not an integer." >&2
    exit 1
    fi

还可以使用bash自己的&&,这个遵循短路原则

1
2
3
if [ condition ] && [ condition ]; then
command
fi

等同样例

1
[[ -d "$dir_name" ]] && cd "$dir_name" && rm *

等同于

1
2
3
4
5
6
7
8
9
10
11
12
if [[ ! -d "$dir_name" ]]; then
echo "No such directory: '$dir_name'" >&2
exit 1
fi
if ! cd "$dir_name"; then
echo "Cannot cd to '$dir_name'" >&2
exit 1
fi
if ! rm *; then
echo "File deletion failed. Check results" >&2
exit 1
fi

函数

函数优于脚本,低于别名

1
2
3
hello() {
echo "Hello $1"
}

函数与变量相关的$@$0等,与脚本一致。

Reference

  1. https://www.ibm.com/docs/en/i/7.3?topic=expansions-parameter