# 扎好马步

  • 双引号里面可以写变量$x,单引号会将$x 当成字符串

# 自定义变量

# 变量的定义

  • 变量是由任何字母、数字和下划线组成的字符串,且不能以数字开头

  • 严格区分大小写

  • 变量、等号、值中间不能出现任何空格

  • 申明的变量默认都会按照字符串来定义,也就是说可以不加引号,但如果字符串内容有空格那么需要使用单双引号定义

# 变量的引用

直接$var 即可

#!/bin/bash
#

var1=hello
var2=‘hello world’

echo $var1
echo $var2

# 位置变量

当一条命令或者脚本执行时,后面可以跟很多参数,我们使用位置参数变量来表示这些参数

  • 位置变量存在与 shell 当中可以直接使用

# 位置变量参数

位置参数变量 含义
$n n 为数字,$0代表脚本本身,$1 ~$9代表1~9个参数。10以上的参数需要用打括号包含,如${10}
$@ 命令行所有参数,但每个参数区别对待
$* 命令行所有参数,所有参数视为一个整体
$# 参数个数
$$ 输出最近进程的 pid
#!/bin/bash
#

echo "第一个参数: $1"
echo "第二个参数: $2"

echo $*
echo $@
echo $#

function add
{
    value=`expr $1 + $2`
    echo $value
}

add $1 200

sh var.sh 1 2 3 输出结果

第一个参数: 1
第二个参数: 2
1 2 3
1 2 3
3
201

# 环境变量

# 环境变量的定义

  • Linux 是一个多租户的操作系统,针对不同的用户会有一个专有的运行环境

  • 不同用户的专有环境就是一组默认环境变量的组合(简单理解)

  • 环境变量默认使用大写定义

# 环境变量的分类

  • 对所有用户生效的环境变量 /etc/profile

  • 对特定用户生效的环境变量 ~/.bashrc 或者~/.bash_profile (没有该文件的话可以自行建立)

  • 临时有效的环境变量 脚本或命令使用 export

# 系统默认的环境变量

  • 通过命令行 $环境变量 可以输出
环境变量 含义
PATH 命令搜索的路径
HOME 用户家的目录路径
LOGNAME 用户登录名
PWD 当前所在路径
HISTFILE 历史命令的保存文件
HISTSIZE 历史命令保存的最大行数
HOSTNAME 主机名
SHELL 用户当前使用的 SHELL
PS1 一级命令提示符
TIMEOUT 用户和系统交过程的超时值
IFS 系统输入分隔符(默认空格或者 tab 或者换行符\n)
OFS 系统输出分隔符
#!/bin/bash
#

echo $HOME
echo $PWD

# 管道

# 管道的定义

将上一个命令的输出作为下一个命令的输入,使用 | 连接两个命令。从某种意义上来说,是重定向的简易实现

以下为找出 brew 包中所有名称带有 open 的 / 列出 brew 包中所有名称带有 open 的行数

brew list | grep open

brew list | grep open | wc -l

# 退出状态码

# 退出状态码的定义

  • 所有的 shell 命令都使用退出状态码来告知 shell 它已经执行完毕

  • 退出状态码是以上一条指令的返回结果为准

  • 退出状态码是一个 0 ~ 255 的整数值

-Linux 提供了一个$?来捕获退出状态码的值

# 退出状态码含义(记住 0 和非 0 即可)

状态码 含义
0 命令成功结束
1 一般性未知错误
2 不适合的 shell 命令
126 命令不可执行
127 没找到命令
128 无效的退出参数
128+x 与 Linux 命令 x 相关的严重错误
130 通过 ctrl+c 终止的命令
255 正常范围之外的退出码
asd

echo $? // 127
#!/bin/bash
#

date
if [ $? -eq 0 ]; then
        echo 'success'
else
        echo 'failed'
fi

# 改变退出状态码的 exit 命令

  • exit 会直接导致退出脚本
// exit $exit + 数值

exit $exit 48
#!/bin/bash
#

date123
if [ $? -eq 0 ]; then
        echo 'success'
else
        echo 'failed'
        exit 25
fi

echo $?

PS:退出脚本后输出 echo $? 显示 25

# if-then / if-then-else

# 语法

  • if 后面一定有空格

  • if command | condition 后面添加 &> /dev/null,将 command | condition 重定向到垃圾桶中,不会改变返回状态码,也不会在 shell 中输出 command | condition 产生的值

if command | condition &> /dev/null
then
    conmmands
else
    conmmands
fi
#!/bin/bash
#

if echo '123' &> /dev/null
then
    echo 'success'
fi

不会输出 123

# 嵌套 if

  • if 和 then 放在一排必须有;分割

# 语法

if command | condition
then
    conmmands
elif command | condition
then
    conmmands
fi

if command | condition;then
    conmmands
elif command | condition;then
    conmmands
fi

# if 的条件判断

  • 条件判断的判断内容必须写在[]里面,并且[ $1, $2 ]开始和结尾要留有空格(运算符前后必须有空格)
#!/bin/bash
#

if [ $1 -eq $2 ]; then
    echo 'success'
fi

# 数值比较

数值比较 含义
n1 -eq n2 n1 === n2 ? true : false
n1 -ne n2 n1 !== n2 ? true : false
n1 -gt n2 n1 > n2 ? true : false
n1 -ge n2 n1 >= n2 ? true : false
n1 -lt n2 n1 < n2 ? true : false
n1 -le n2 n1 <= n2 ? true : false

# 字符串比较

  • 字符串比较时候和 js 不同,没有===这种说法

  • 输入< >符号比较的时候需要转义,因为这两个符号在 Linux 中表示重定向

#!/bin/bash
#

a='qq'
b='vv'
if [ $a \< $b ]; then
    echo 'equal'
else
    echo 'not equal'
fi
字符串比较 含义
str1 = str2 相等比较
str1 != str2 不等比较
str1 < str2 str1 < str2 为 true (比较第一个字母的字母表的顺序,越前越大)
str1 > str2 str1 > str2 为 true (比较第一个字母的字母表的顺序,越前越大)
-n str1 str1 长度不为 0 则返回 true
-z str1 str1 长度为 0 则返回 true

PS:-n 和 -z 是检查字符串是否定义的,值得注意的是,没有定义的值检查必须加双引号

#!/bin/bash
#

b='vv'
if [ -n "$a" ]; then
    echo 'not kong'
else
    echo 'kong'
fi

# 文件比较

文件比较 含义
-d file file 是否为目录
-f file file 是否为文件
-e file file 是否存在
-r file file 是否可读
-w file file 是否可写
-x file file 是否可执行
-s file file 存在且非空
file1 -nt file2 file1 比 file2 更新为真
file1 -ot file2 file1 比 file2 更旧为真
if [ -f index.js ];then echo 1;else echo 2; fi

# if 的复合条件测试

if [ condition1 ] && [ condition2 ]
then
    conmmands
else
    conmmands
fi

if [ condition1 ] && [ condition2 ]
then
    conmmands
else
    conmmands
fi
#!/bin/bash
#

a=5
b=1
c=3
d=4
if [ $a -lt $b ] && [ $c -eq $d ]; then
    echo 'yes'
else
    echo 'no'
fi

PS:注意&&是在两个[]之间的而不是写在[]里面

# if-then 中使用双括号

  • 不止 if-then 中使用,shell 脚本中也可以使用双括号

  • 使用双括号可以进行算术运算

  • 双括号中,变量引用可以加$也可以不加(这里注意,单独的双括号是不能加$使用变量的,但是在条件语句后面的就可以)


num=10

while (($num < 20 )); do # 这里都可以
    echo 'number is' $num
    ((num++)) # 这里不能够加
done
  • 运算符前后可以有空格也可以没有

  • 双括号不止可以用于 if,还可以用于 for、while 等循环结构中

  • 多个运算表达式使用逗号分割

以下是传统写法和双括号写法

if [ -f file ]
then
    conmmands
fi

if ((expression))
then
    conmmands
fi

示例

#!/bin/bash
#

c=3
d=4
if ((d < c)); then
    echo 'yes'
else
    echo 'no'
fi

a=10

((a++,a=a-10))

echo $a

# 以下三种方式是等价的

if (($1 > $2 && $3 > $2)); then
    echo 'yes'
else
    echo 'no'
fi

if [ $1 > $2 ]  && [ $3 > $2 ] ; then
    echo 'yes'
else
    echo 'no'
fi

if [ $1 ] >$2 && [ $3 ] >$2; then
    echo 'yes'
else
    echo 'no'
fi

可用的运算符

运算符 含义
value++/-- 后增/减
++/--value 先增/减
! 逻辑求反
== 相等
>/< 大于/小于
>=/<= 大于等于/小于等于
&& 逻辑与
|| 逻辑或

# if-then 中使用双方括号

  • 变量名必须引用$

  • 变量之间可以不加空格但是[[]]中首位必须加空格

# 语法

if [[ $n1 -gt $n2 && $n2 -lt $n3 ]]
then
    conmmands
fi
#!/bin/bash
#

if
    [[ $1 -gt $2 && $2 -lt $3 ]]
then
    echo 'yes'
else
    echo 'no'
fi

PS:macOS 中不能直接使用<>符号判断

# case 命令

  • *)相当于 default

# 语法

case $var in
    parttern1)
        commands
    parttern2)
        commands
    *)
        ommands
esac
#!/bin/bash
#

case $1 in
ljc)
    echo 'ljc'
    ;;
cw)
    echo 'cw'
    ;;
*)
    echo 'love'
    ;;
esac

# for 循环

# 简介

循环遍历一系列特定值,然后再结构体中针对每个特定值做处理

# 语法

  • list 可以是字符串、枚举、甚至是 ls 的返回值

  • 遍历是通过系统分隔符 IFS(默认是空格)来分割值的,也就是说类似与 list='ljc cw xw chx'这种就相当于 js 中的[ljc,cw,xw,chx]

# 读取变量、列表的值

for $var in list
do
    command
done
for i in ljc cw zc lzp
do
    echo $i
done
for i in {1..20}
do
    echo $i
done
list='ljc cw xw chx'
for i in $list
do
    echo $i
done
IFS=":"
list='ljc cw xw chx'
# list='ljc:cw:xw:chx'
for i in $list
do
    echo $i
done
# 系统分隔符改为: 则list会整个被当成一个字符串输出,将注释打开则和上一个例子相同

# 读取命令执行结果的值

  • 使用命令替换$(command) 将命令输出的结果作为变量的值(输出)

  • 软连接如果指向目录那么检测出来的也属于目录

FILE=$(ls)
for i in $FILE; do
    if [ -d $i ]; then
        echo "$i is dir"
    elif [ -f $i ]; then
        echo "$i is file"
    fi
done

# 两者等价

for i in $(ls); do
    if [ -d $i ]; then
        echo "$i is dir"
    elif [ -f $i ]; then
        echo "$i is file"
    fi
done

假如我们有一个文本

ljc love cw
zc
lzp
IFS=$'\n'

for i in $(cat ./case/for/text.txt); do
    echo $i
done

得到结果

ljc love cw
zc
lzp

如果不更改IFS则输出

ljc
love
cw
zc
lzp

# c 语言风格的 for 命令

# 语法

for ((i = 1; i < 10; i++))
do
    command
done
for ((i = 0; i <= 100; i++)); do
    ((sum += $i))
done

echo $sum

# 注意要用c语言的计算风格必须使用双() shell中变量定义只有顺序范围,所以在里面定义sum外面也可以拿到

# while 循环

  • 条件语句的使用方法和 if 是一样的,可以使用[]也可以(())

# 语法

while command
do
    commands
done
num=10

while ((num < 20 )); do
    echo 'number is' $num
    ((num++))
done

while true; do
    echo '死循环'
done

# until 命令

  • 和 while 相反,知道满足某个条件的时候才会终止循环
until command
do
    commands
done
num=10

until [ $num -eq 0 ]; do
    echo 'stop ' $num
    ((num--))
done

# 控制循环的 break 指令

  • 和 js 类似,但没有类似 let 的赋值作用域,所以多个 for 循环嵌套不能用相同的变量,必须使用 i,j,k 等

  • break + num 表示跳出几个循环,默认为 1

  • shell 日常使用中是不建议使用多个嵌套 for 循环的

for ((i = 0; i <= 5; i++)); do
    echo '外' $i
    for ((j = 10; j <= 20; j++)); do
        echo '内' $j
        if ((j > 15)); then
            break 2
        fi
    done
done

echo $sum

# 控制循环的 continue 指令

  • 表示不执行满足条件的循环轮次,并不是全部终止循环

  • continue + num 表示跳出几个循环层次,默认为 1,不经常用

for ((i = 0; i <= 20; i++)); do
    if ((i > 10 && i < 15)); then
        continue
    else
        echo "输出$i"
    fi
done

# 处理循环的输出

  • 使用重定向 > 将结果输出到一个文件中
for ((i = 0; i <= 20; i++)); do
    echo "$i"
done > ./case/for/result.txt
  • 可以通过管道对循环输出的结果做处理
for ((i = 0; i <= 20; i++)); do
    echo "$i"
done | grep 5 # 查看包含5的结果