Bash Shell 常用语法和操作

变量

变量名=值 :

a="abc"
echo "$a"

数组

定义一个数组变量

range

语法:

{start..end..step}

示例:

# 输出 1 2 3
for value in {1..3}
do
    echo $value
done

# 输出 1 3 5
for value in {1..6..2}
do
    echo $value
done

if

if 语句的语法如下:

if [ <some test> ]
then
    <commands>
elif [ <some test> ]
then
    <different commands>
else
    <other commands>
fi

比如:

if [ "$age" -gt 18 ]
then
    echo "you are $age > 18"
elif [ "$age" -eq 18 ]
then
    echo "you are $age == 18"
else
    echo "you are $age < 18"
fi

常用的条件判断

条件表达式 含义
! 表达式 false
-n 字符串 字符串长度大于 0
-z 字符串 字符串长度等于 0
A = B 字符串 A B 相等
A != B 字符串 A B 不相等
m -eq n 数字 m n 相等
m -gt n 数字 m 大于 n
m -lt n 数字 m 小于 n
-d 路径 文件路径存在,并且是个目录
-e 路径 文件存在
-s 路径 文件存在,并且大小大于 0
-r 路径 文件存在,并且拥有读权限
-w 路径 文件存在,并且拥有写权限
-x 路径 文件存在,并且拥有执行权限

[] 实际调用的是 test 命令,我们可以用 test 命令来测试条件表达式:

$ test "abc" == 18
$ echo $?
1
$ test 1 -eq 1
$ echo $?
0

以及可以用 &&|| 组合判断多个条件表达式,实现 and 和 or 的功能

case(switch case)

语法:

case <variable> in
<pattern 1>)
    <commands>
    ;;
<pattern 2>)
    <other commands>
    ;;
esac

例子:

case $1 in
start)
    echo starting
    ;;
stop)
    echo stoping
    ;;
restart)
    echo restarting
    ;;
*)
    echo don\'t know
    ;;
esac

for

语法:

for var in <list>
do
    <commands>
done

同时 for 也支持 breakcontinue

例子:

names='tom jim jake'
for name in $names
do
    echo $name
done
for age in {1..10}
do
    if [ $age -eq 3 ]
    then
        continue
    fi

    echo $age

    if [ $age -gt 5 ]
    then
        break
    fi
done

while

语法:

while [ <some test> ]
do
    <commands>
done

例子:

total=1
while [ $total -le 3 ]
do
    echo $total
    ((total++))
done

until

语法:

until [ <some test> ]
do
    <commands>
done

例子:

total=1
until [ $total -gt 3 ]
do
    echo $total
    ((total++))
done

select 单项选择

选择功能一般用 case 语法用的多,不过 select 也可以实现, select 的功能类似 while + case,比较适合反复询问用户选择项直到用户选择了输入了合法的选择项。

语法:

select var in <list>
do
    <commands>
    [break]
done

可以用 break 跳出 select 以及可以通过修改 PS3 变量的值修改提示内容。

示例:

PS3='please choice: '

select value in {1..5}
do
    echo $value

    if [ $value -gt 3 ]
    then
        break
    fi

done

函数

语法:

function_name () {
    <commands>
}

# or

function function_name {
    <commands>
}

参数

在函数体内可以通过 $1, $2, $3, ... 获取调用时传入的位置参数

返回值

不支持返回值功能,不过可以用 return x 指定一个状态吗:

function test {
    echo "test"
    return 3
}

test
echo $?   # 3

变量作用域

默认函数类的变量是全局变量,可以通过 local 定义局部变量:

function test {
    local a='local'

    a='233'
    b='666'
    echo $a, $b
}

b='global'

test

echo $b
echo $a

创建跟外部命令同名的函数

例子:

function ls {
    command ls -lh
}

ls

主要是不要忘了加 command,不加的话,上面的 ls 会无限递归不会按预期的调用 ls 命令。

字符串操作

字符串替换

替换子串

$ test="hello_world"
$ echo "${test/_/-}"
hello-world
$ echo "${test/world/bar}"
hello_bar

删除前缀

$ test="foofoo_bar_foofoo"
$ echo "${test#foo}"
foo_bar_foofoo
$ prefix="foo"
$ echo "${test#$prefix}"
foo_bar_foofoo

删除后缀

$ test="foofoo_bar_foofoo"
$ echo "${test%foo}"
foofoo_bar_foo
$ suffix="foo"
$ echo "${test%$suffix}"
foofoo_bar_foo

打印字符串

推荐使用 printf:

printf "hello world\\n"
printf "hello %s\\n" "world"

echo 也可以:

echo hello

文件操作

保存大段文本

可以使用 teeEOF:

tee test.text <<EOF
hello
    world

!

EOF

保存包含 EOF 命令的文本可以用多个 EOF 的变种:

tee a.sh << EOF
tee b.text << EOF2
 echo hello
EOF2
EOF


$ tee a.sh << EOF
> tee b.text << EOF2
> echo hello
> EOF2
> EOF
tee b.text << EOF2
echo hello
EOF2

$ cat a.sh
tee b.text << EOF2
echo hello
EOF2

$ bash a.sh
echo hello

$ cat b.text
echo hello

append 更新可以使用 tee -a

其他实用功能

隔离 cd 命令的干扰

可以用括号来隔离:

# pwd=/tmp
(
    cd test/
    ls   # pwd = /tmp/test/
)
# pwd=/tmp

设置功能标志

一般会在脚本中设置如下标志:

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

常用的功能标志如下:

  • 运行的时候显示执行到那一条语句了: set -x
$ cat a.sh
set -x

echo 'hello'

$ bash a.sh
+ echo hello
hello

bash -x xx.sh 也可以实现同样的效果:

$ cat a.sh
echo 'hello'

$ bash -x a.sh
+ echo hello
hello
  • 有任何命令的退出码不是 0 就退出: set -e
$ cat a.sh
abc
echo 'hello'

$ bash a.sh
a.sh: line 1: abc: command not found
hello

$ cat a.sh
set -e
abc
echo 'hello'

$ bash a.sh
a.sh: line 2: abc: command not found

bash -e xx.sh 也可以实现同样的效果。

  • 引用未定义的变量时出错并退出: set -u
$ cat a.sh
echo "$abc"
echo hello

$ bash a.sh

hello

$ cat a.sh
set -u
echo "$abc"
echo hello

$ bash a.sh
a.sh: line 2: abc: unbound variable

bash -u xx.sh 也可以实现同样的效果。

  • 管道命令中有一个命令失败整个管道操作的退出码将是失败的那个命令的退出码, 未设置这个标志时是最后一个操作的退出码: set -o pipefail
$ cat a.sh
la la la | echo 'aa'

$ bash a.sh
a.sh: line 1: la: command not found
aa

$ echo $?
0

$ cat a.sh
set -o pipefail
la la la | echo 'aa'

$ bash a.sh
a.sh: line 2: la: command not found
aa

$ echo $?
127
  • 设置单词切分使用的分隔符,默认是 $' \n\t' (空格、换行、制表符), 可以通过 IFS=$'\n\t' 设置为换行和制表符:
$ cat a.sh
names=(
    "foo bar"
    "foobar tom"
)

for i in ${names[@]}; do
    echo "$i"
done

$ bash a.sh
foo
bar
foobar
tom

$ cat a.sh
names=(
    "foo bar"
    "foobar tom"
)

IFS=$'\n\t'
for i in ${names[@]}; do
    echo "$i"
done

$ bash a.sh
foo bar
foobar tom

更多的 set 支持的标志可以查看 Bash Reference Manual

数学运算

可以通过 $(( <expression> )) 执行数学运算,比如:

  • +: $(( 1 + 2 ))
  • -: $(( 1 - 2 ))
  • *: $(( 1 * 2 ))
  • /: $(( 1 / 2 ))
  • %: $(( 1 % 2 ))

支持变量做运算符,比如: $(( a + b )) (推荐) 或 $(( $a + $b )) (不推荐, 详见 SC2004 - shellcheck


Comments