入门shell命令

关注微信公众号塔容万物
ls
top

与平时所使用的单条Linux不同,shell脚本可以看作是多条Linux命令的组合(在一个文件里面写多条Linux命令)。事实上,shell并不是简单的多条命令的堆积,而是具有编程功能,比如它可以接收参数,可以进行判断和循环,还支持编写函数。下面就是一个统计对应文件行数的shell脚本,可以通过bash test.sh file来检查file这个文件的行数。在检查行数之前,脚本还检查了是否指定了file,指定的file是否存在,以确保下面的统计行数是可以正常执行的。

# test.sh
if [ $# -ne 1 ]; then
    echo "Usage: $0 filename"
    exit 0
fi

if [ ! -f $1 ]; then
    echo "File \`$1\` does not exist."
    exit 0
fi

line_number=$(cat $1 | wc -l)
echo File [${1}] has ${line_number} lines.

变量

变量被用来存储值,比如上面的line_number就是用来存储行数的,在shell中,变量的形式为变量名=变量值,比如a=1。等号=的左右两边都不能有空格,否则会报错。等号的作用为赋值,即将1赋给a,在shell中,变量值只有一种类型,即所有的变量值都是字符串类型。

a=1
b=2
c=apple

访问一个变量时,可以通过$变量名/${变量名}来进行访问

a=1
b=2
echo $a
echo ${b}

echo是一个shell中内置的函数,它可以把后面的内容打印出来

注释

shell中使用#作为注释符号,任何#后面的内容都是注释,注释只用作展示,而不会被运行。

a=1  # 将1赋给a
# 下面是将2赋给b
b=2

引号

在shell中引号有三种

""
''
``

""双引号可以用来作为模板字符串来使用,可以在字符串中使用$符号来讲对应的变量值填充到对应的位置。在使用echo输出内容时,默认使用的就是双引号

name=Jack
message="my name is ${name}"
echo "$message"
# my name is Jack

''单引号是静态字符串,被单引号所包裹的内容无法用作模板字符串,一半用来放置多行文本

name=Jack
message='my name is ${name}'
echo "$message"
# my name is ${name}

long_desc='This is a long description.

1. xxxxxx
2. yyyyy'
echo $long_desc
# This is a long description.
#
# 1. xxxxxx
# 2. yyyyy

反引号是可执行字符串,被他包裹的一般是命令

echo `pwd` # pwd会首先被执行

上面这三种引号中,双引号不会打印$,单引号全都打印,反引号则是执行包含的内容。但有些时候,可能需要双引号打印$符号,就可以使用转义符\来进行转义

name=jack
a="\$${name}"
echo $a  # $jack

b="'aaaa"
echo $b  # 'aaaa

$符号

$符号可以被用来访问变量,除此之外,它还可以用来执行命令,方法是使用$(命令),与反引号的功能一样

echo $(pwd)
echo `pwd`

printf

你可能已经发现,前面用到的echo会自动进行换行,也就是说,下面的代码在运行以后,会输出两行

echo "line1"
echo "line2"
# line1
# line2

这是因为echo在每次打印的时候,都会在输出的内容上在打印一个换行符\n.printf与echo的功能相似,但不会打印换行符\n

printf "line1"
printf "line2"
# line1line2

要想使得printf的功能与echo一样,可以手动的在每个字符串结尾加一个\n

printf "line1\n"
printf "line2\n"

运算

shell也可以进行数字运算 expr是shell中的运算指令,支持一般的数学运算 使用方法是expr后跟上要运算的表达式

a=1
b=2
printf 'expr $a + $b: '
echo `expr $a + $b`
printf '$(expr $a + $b): '
echo $(expr $a + $b)

判断

shell中支持if-else判断,使用方法如下

if [ 条件 ]; then
    # 条件成立时执行的代码
else
    # 条件不成立时执行的代码
fi

条件的写法有很多种,比如

判断数字相同

shell中对数字的判断有专门的判断符号,

ne # !=
eq # ==
gt # >
ge # >=
lt # <
le # <=
if [ 1 -eq 1 ]; then
    echo "1 == 1"
else
    echo "1 != 1"
fi

判断文件是否存在

只能判断文件

if [ -f ./test.sh ]; then
    echo "test.sh exists"
else
    echo "test.sh does not exist"
fi

判断目录是否存在

只能判断目录

if [ -d ./test ]; then
    echo "test exists"
else
    echo "test does not exist"
fi

判断文件/目录是否存在

-e可以理解为-d-f的结合

if [ -e ./test ]; then
    echo "test exists"
else
    echo "test does not exist"
fi

判断字符串是否相同

a='apple'
b='apple'
if [ $a = $b ]; then
    echo "a == b"
else
    echo "a != b"
fi

对判断结果取反

a=1
b=2
if [ ! $a = $b ]; then
    echo "a != b"
else
    echo "a == b"
fi

判断字符串是否为空

-z是判断字符串是否为空的符号,如果字符串为空,则返回true,否则返回false

a='apple'
if [ -z $a ]; then
    echo "a is empty"
else
    echo "a is not empty"
fi

使用正则

shell中可以使用=~来进行正则匹配,这里的判断条件是正则表达式,与一般的判断条件不同,正则表达式需要用两个中括号[[ 判断条件 ]]包裹起来

if [[ "hello" =~ ^h.* ]]; then
    echo "hello"
else
    echo "not hello"
fi

数组

shell中支持数组,使用方法如下

数组名=(元素1 元素2 元素3)

数组使用()包裹,元素之间使用空格进行分割

按照下表进行访问数组

shell中的小标是从1开始的

arr=(1 2 3 'apple')
echo ${arr[1]} # 1
echo ${arr[2]} # 2
echo ${arr[3]} # 3
echo ${arr[4]} # apple

访问所有元素

arr=(1 2 3)
echo ${arr[@]}

访问数组长度

arr=(1 2 3)
echo ${#arr[@]}

截取数组

arr[@]:start:count表示从下标为start+1的元素开始,截取count个元素

arr=(1 2 3)
echo ${arr[@]:1:2}

修改数组元素

arr=(1 2 3)
arr[3]=4
echo ${arr[@]}

合并数组

arr=(1 2 3)
arr2=(4 5 6)
arr3=(${arr[@]} ${arr2[@]})
echo ${arr3[@]}

循环

shell中支持for循环,使用方法如下

for 变量 in 取值列表; do
    # 循环体
done

常见的是对数据进行循环

arr=(a b c d e f)
for i in ${arr[@]}; do
    echo "element: ${i}"
done

也可以直接对一组数据进行循环

for i in 1 2 3 4 5; do
    echo $i
done

循环也支持使用break和continue,break表示中断跳出循环,continue表示跳过本次循环

for i in 1 2 3 4 5; do
    echo $i
    if [ $i -eq 3 ]; then
        break
    else
        echo "This line will be printed"
        continue
        echo "This line will not be printed"
    fi
done

输出

1
This line will be printed
2
This line will be printed
3

函数

shell中支持函数,使用方法如下

function 函数名() {
    # 函数体
}

比如

function hello() {
    echo "hello"
}
hello

函数参数

函数使用$1$2$3等来访问参数,$0表示函数名,$#表示参数个数,$@表示所有参数

function hello() {
    echo "function name: $0"
    echo "argument count: $#"
    echo "argument list: $@"
    echo "hello $1"
}
hello "Jack" 1 2 3

对应的输出结果

function name: hello
argument count: 4
argument list: Jack 1 2 3
hello Jack

退出

shell中可以使用exit来退出当前的shell

exit 数字

数字必须是0-255中的一个数字,0表示正常运行,1-255表示出现了错误

exit 1
exit 0

unset

shell中可以使用unset来删除变量

name=Jack
echo $name
unset name
echo $name

hashbang

在shell脚本的第一行叫做hashbang,可以使用#!来指定当前脚本的解释器,常见的shell解析器有zsh,bash,sh等

#!/bin/bash
echo "hello"
#!/bin/zsh
echo "hello"

使用环境变量

shell中可以使用$变量名来访问环境变量

echo $PATH
echo $HOME
echo $PATH
echo $PWD
echo $SHELL
echo $USER

设置某个环境变量

shell中可以使用export来设置某个环境变量

export name=Jack
echo $name

永久设置环境变量

shell中可以使用~/.bashrc来永久设置环境变量

echo $PATH
echo 'export PATH=$PATH:/usr/local/bin' >> ~/.bashrc
echo $PATH

上面这段代码的作用是将/usr/local/bin添加到PATH环境变量中。每次打开一个shell,都会执行~/.bashrc中的代码,这样写可以永久的将/usr/local/bin添加到PATH环境变量中。环境变量的PATH中路径通过:进行分割,$PATH:/usr/local/bin来表示将/usr/local/bin添加到PATH环境变量中。

通配符

shell中可以使用*来进行通配符匹配

ls *.txt
# 匹配所有的txt文件
ls hello*
# 匹配所有以hello开头的文件
ls *hello*
# 匹配所有包含hello的文件

字典

shell中可以使用declare来定义字典

declare -A dict
dict=([name]=Jack [age]=18)
echo ${dict[name]}
echo ${dict[age]}
declare -A dict
dict=([name]=Jack [age]=18)
echo ${dict[@]}
declare -A dict
dict=([name]=Jack [age]=18)
echo ${#dict[@]}

$?

shell中可以使用$?来获取上一条命令的返回值

ls
echo $?

检查shell解释器是否支持字典

shell中可以使用declare -A来检查shell解释器是否支持字典

declare -A dict
if [ $? -eq 0 ]; then
    echo "support dict"
else
    echo "not support dict"
fi

declare的作用

shell中可以使用declare来定义变量的作用域

function hello() {
    declare name=Jack
    echo $name
}