[Acwing Linux基础课] 第三讲 shell

目录

运行方式

变量类型

传递参数

数组

获取数组中的所有元素

运算符

 expr 表达式

关系运算符

字符串表达式

read命令

echo命令

显示换行

显示不换行

原样输出字符串,不进行转义或取变量(用单引号)

printf 命令(用于格式化输出)

逻辑运算符&&和||

test命令 

 字符串比较

多重条件判定

判断符号[]

流程控制

判断语句

if

if else

if else-if else

case…esac形式

for…in…do…done

for ((…;…;…)) do…done

while…do…done循环

until…do…done循环

break命令

continue命令

死循环的处理方式

函数

函数内的局部变量

exit命令

 重定向

引入外部脚本


 


#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。

运行方式

新建一个test.sh文件,内容如下:

#! /bin/bash
echo "Hello World!"

作为可执行文件

acs@9e0ebfcd82d7:~$ chmod +x test.sh  # 使脚本具有可执行权限
acs@9e0ebfcd82d7:~$ ./test.sh  # 当前路径下执行
Hello World!  # 脚本输出
acs@9e0ebfcd82d7:~$ /home/acs/test.sh  # 绝对路径下执行
Hello World!  # 脚本输出
acs@9e0ebfcd82d7:~$ ~/test.sh  # 家目录路径下执行
Hello World!  # 脚本输出

用解释器执行

acs@9e0ebfcd82d7:~$ bash test.sh
Hello World!  # 脚本输出

变量类型

当一个shell运行,存在三种主要类型的变量:

  • 自定义变量(局部变量)        子进程不能访问的变量

  • 环境变量(全局变量)        子进程可以访问的变量

自定义变量改成环境变量

name=ert  # 定义变量

export name  # 第一种方法

declare -x name  # 第二种方法

环境变量改为自定义变量

export name=ert  # 定义环境变量

declare +x name  # 改为自定义变量

单引号与双引号的区别

单引号中的内容会原样输出,不会执行、不会取变量;
双引号中的内容可以执行、可以取变量;

获取字符串长度

name="qwer"
echo ${#name}  # 输出4


提取子串

name="hello, qwer"
echo ${name:0:5}  # 提取从0开始的5个字符

传递参数

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

为脚本设置可执行权限,并执行脚本,输出结果如下所示:

$ chmod +x test.sh 
$ ./test.sh 1 2 
Shell 传递参数实例!
执行的文件名:./test.sh
第一个参数为:1
第二个参数为:2
第三个参数为:

另外,还有几个特殊字符用来处理参数:

参数处理说明
$#传递到脚本的参数个数
$*以一个单字符串显示所有向脚本传递的参数。
如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$@与$*相同,但是使用时加引号,并在引号中返回每个参数。
如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$-显示Shell使用的当前选项,与set功能相同。
$?★显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

$(command)

或者`command`

★返回command这条命令的stdout(可嵌套)

数组

my_array=(A B "C" D)

我们也可以使用下标来定义数组:

array_name[0]=value0
array_name[1]=value1
array_name[2]=value2

读取数组元素值的一般格式是:

${array_name[index]}

获取数组中的所有元素

使用@ 或 * 可以获取数组中的所有元素,

例如:

a=(23 4f fg y)

echo "数组a的元素为: ${a[*]}"
echo "数组a的元素为: ${a[@]}"

执行脚本,输出结果如下所示:

$ chmod +x test.sh 
$ ./test.sh
数组的元素为: 23 4f fg y
数组的元素为: 23 4f fg y

接下来

a[100]=pp

echo "数组a的元素为: ${a[*]}"
echo "数组a的元素个数为: ${#a[*]}"

 ★输出如下:

数组的元素为: 23 4f fg y pp

数组的元素个数为: 5

运算符

优先级: 字符串表达式 > 算术(expr)表达式 > 逻辑关系表达式

 expr 表达式

表达式说明:

  • 用空格隔开每一项
  • 用反斜杠放在shell特定的字符前面(发现表达式运行错误时,可以试试转义)
  • 对包含空格和其他特殊字符的字符串要用引号括起来
  • expr会在stdout中输出结果。如果为逻辑关系表达式,则结果为真,stdout为1,否则为0。
  • expr的exit code:如果为逻辑关系表达式,则结果为真,exit code为0,否则为1。

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。

expr 是一款表达式计算工具,使用它能完成表达式的求值操作。

例如,两个数相加(注意使用的是反引号 ` 而不是单引号 '):

a=10
b=20

echo "a + b = `expr $a + $b`"
echo "a - b = `expr $a - $b`"
echo "a * b = `expr $a \* $b`"
echo "a - b = `expr $a - $b`"
echo "b / a = `expr $b / $a`"
echo "b % a = `expr $b % $a`"
echo "(a+1)*(b+1)=`expr \( $a + 1 \) \* \( $b + 1 \)`"  # 值为(a + 1) * (b + 1)

if [ $a == $b ]
then
   echo "a 等于 b"
fi
if [ $a != $b ]
then
   echo "a 不等于 b"
fi

 [$a==$b] 是错误的,必须写成 [ $a == $b ]

执行脚本,输出结果如下所示:

a + b = 30
a - b = -10
a * b = 200
b / a = 2
b % a = 0
(a+1)*(b+1)=231
a 不等于 b

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

(equal,greater than,less than...)

运算符说明举例(a=10,b=20)
-eq==[ $a -eq $b ] 返回 false。
-ne

!=

[ $a -ne $b ] 返回 true。
-gt>[ $a -gt $b ] 返回 false。
-lt<[ $a -lt $b ] 返回 true。
-ge>=[ $a -ge $b ] 返回 false。
-le<=[ $a -le $b ] 返回 true。
a=3
b=4

echo `expr $a \> $b`  # 输出0,>需要转义
echo `expr $a '<' $b`  # 输出1,也可以将特殊字符用引号引起来
echo `expr $a '>=' $b`  # 输出0
echo `expr $a \<\= $b`  # 输出1

c=0
d=5

echo `expr $c \& $d`  # 输出0
echo `expr $a \& $b`  # 输出3
echo `expr $c \| $d`  # 输出5
echo `expr $a \| $b`  # 输出3

字符串表达式

  • length STRING         返回STRING的长度
  • index STRING  CHARSET              CHARSET中任意单个字符在STRING中最前面的字符位置,下标从1开始。如果在STRING中完全不存在CHARSET中的字符,则返回0。
  • substr STRING POSITION LENGTH       返回STRING字符串中从POSITION开始,长度最大为LENGTH的子串。如果POSITION或LENGTH为负数,0或非数值,则返回空字符串。

示例:

str="Hello World!"

echo `expr length "$str"`  # ``不是单引号,表示执行该命令,输出12
echo `expr index "$str" aWd`  # 输出7,下标从1开始
echo `expr substr "$str" 2 3`  # 输出 ell

read命令

echo命令

显示换行

echo -e "Hi\n"  # -e 开启转义
echo "acwing"

输出结果:

Hi

acwing


显示不换行

echo -e "Hi \c" # -e 开启转义 \c 不换行
echo "acwing"

输出结果:

Hi acwing

原样输出字符串,不进行转义或取变量(用单引号)

name=acwing
echo '$name\"'

输出结果

$name\"

printf 命令(用于格式化输出)

printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876

printf "%d * %d = %d\n"  2 3 `expr 2 \* 3` # 表达式的值作为参数

执行脚本,输出结果如下所示:

姓名     性别   体重kg
郭靖     男      66.12
杨过     男      48.65
郭芙     女      47.99
2 * 3 = 6

逻辑运算符&&和||

&& 表示与,|| 表示或
二者具有短路原则:
expr1 && expr2:当expr1为假时,直接忽略expr2
expr1 || expr2:当expr1为真时,直接忽略expr2
表达式的exit code为0,表示真;为非零,表示假。(与C/C++中的定义相反)

test命令 

-e 文件名如果文件存在则为真
-r 文件名如果文件存在且可读则为真
-w 文件名如果文件存在且可写则为真
-x 文件名如果文件存在且可执行则为真
-s 文件名如果文件存在且非空则为真
-d 文件名如果文件存在且为目录则为真
-f 文件名如果文件存在且为普通文件则为真
-c 文件名如果文件存在且为字符型特殊文件则为真
-b 文件名如果文件存在且为块特殊文件则为真

 字符串比较

测试参数              代表意义
test -z STRING    判断STRING是否为空,如果为空,则返回true
test -n STRING    判断STRING是否非空,如果非空,则返回true(-n可以省略)
test str1 == str2    判断str1是否等于str2
test str1 != str2    判断str1是否不等于str2

多重条件判定

测试参数代表意义
-a两条件是否同时成立
-o两条件是否至少一个成立
!取反。如 test ! -x file,当file不可执行时,返回true

              

判断符号[]

[ ]与test用法几乎一模一样,更常用于if语句中。另外[[ ]]是[]的加强版,支持的特性更多。

例如:

[ 2 -lt 3 ]  # 为真,返回值为0
echo $?  # 输出上个命令的返回值,输出0
acs@9e0ebfcd82d7:~$ ls  # 列出当前目录下的所有文件
>>homework  output.txt  test.sh  tmp
acs@9e0ebfcd82d7:~$ [ -e test.sh ] && echo "exist" || echo "Not exist"
>>exist  # test.sh 文件存在
acs@9e0ebfcd82d7:~$ [ -e test2.sh ] && echo "exist" || echo "Not exist"
>>Not exist  # testh2.sh 文件不存在


注意:

  • [ ]内的每一项都要用空格隔开
  • 中括号内的变量,最好用双引号括起来
  • 中括号内的常数,最好用单或双引号括起来

例如:

name="qwe"
[ $name == "qwe" ]  # 错误,等价于 [ acwing yxc == "acwing yxc" ],参数太多
echo $?
>>1
[ "$name" == "asd" ]  # 正确
echo $?
>>0

流程控制

判断语句

if

if condition
then
    command1 
    ...
    commandN 
fi

if else

if condition
then
    command1 
    ...
    commandN
else
    command
fi

if else-if else

if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

case…esac形式

case $变量名称 in
    值1)
        语句1
        语句2
        ...
        ;;  # 类似于C/C++中的break
    值2)
        语句1
        语句2
        ...
        ;;
    *)  # 类似于C/C++中的default
        语句1
        语句2
        ...
        ;;
esac

for…in…do…done

for var in val1 val2 val3
do
    语句1
    语句2
    ...
done

示例1,输出a 2 cc,每个元素一行:

for i in a 2 cc
do
    echo $i
done

示例2,输出当前路径下的所有文件名,每个文件名一行:

for file in `ls`
do
    echo $file
done

示例3,输出1-10

for i in $(seq 1 10)
do
    echo $i
done

示例4,使用{1..10} 或者 {a..z}

for i in {a..z}
do
    echo $i
done


for ((…;…;…)) do…done

for ((expression; condition; expression))
do
    语句1
    语句2
done

示例,输出1-10,每个数占一行:

for ((i=1; i<=10; i++))
do
    echo $i
done


while…do…done循环

while condition
do
    语句1
    语句2
    ...
done

示例,文件结束符为Ctrl+d,输入文件结束符后read指令返回false。

while read name
do
    echo $name
done


until…do…done循环

当条件为真时结束

until condition
do
    语句1
    语句2
    ...
done

示例,当用户输入yes或者YES时结束,否则一直等待读入。

until [ "${word}" == "yes" ] || [ "${word}" == "YES" ]
do
    read -p "Please input yes/YES to stop this program: " word
done


break命令


跳出当前一层循环,注意与C/C++不同的是:break不能跳出case语句

示例(每读入非EOF的字符串,会输出一遍1-7)

while read name
do
    for ((i=1;i<=10;i++))
    do
        case $i in
            8)
                break
                ;;
            *)
                echo $i
                ;;
        esac
    done
done

该程序可以输入Ctrl+d文件结束符来结束,也可以直接用Ctrl+c杀掉该进程。

continue命令

跳出当前循环

示例(输出1-10中的所有奇数)

for ((i=1;i<=10;i++))
do
    if [ `expr $i % 2` -eq 0 ]
    then
        continue
    fi
    echo $i
done

死循环的处理方式

  1. 如果AC Terminal可以打开该程序,则输入Ctrl+c即可
  2. 否则可以直接关闭进程:使用top命令找到进程的PID, 输入kill -9 PID即可关掉此进程

函数

作者:yxc
链接:https://www.acwing.com/blog/content/9731/

bash中的函数类似于C/C++中的函数,但return的返回值与C/C++不同,返回的是exit code,取值为0-255,0表示正常结束。

  • 如果想获取函数的输出结果,可以通过echo输出到stdout中,然后通过$(function_name)来获取stdout中的结果。
  • 函数的return值可以通过$?来获取。

命令格式:

[function] func_name() {  # function关键字可以省略
    语句1
    语句2
    ...
}


不获取 return值和stdout值

func() {
    name=yxc
    echo "Hello $name"
}

func

输出结果:

Hello yxc


获取 return值和stdout值
不写return时,默认return 0

func() {
    name=yxc
    echo "Hello $name"

    return 123
}

output=$(func)
ret=$?

echo "output = $output"
echo "return = $ret"

输出结果:

output = Hello yxc
return = 123

实践的时候发现返回的值并不是设置的"666",而是154,就离谱,突然想起来exit code 的范围是0-255,我做了如下实验:

  1. 改为return 665,输出了"return=153",我意识到了这个返回值若是超过了255后可能是个循环
  2. 改为return 256,输出了"return=0"
  3. 预测一波,255+256=511,那么返回的是511的话,return=0,验证得出实如此 


 

函数的输入参数
在函数内,$1表示第一个输入参数,$2表示第二个输入参数,依此类推。

注意:函数内的$0仍然是文件名,而不是函数名。

func() {  # 递归计算 $1 + ($1 - 1) + ($1 - 2) + ... + 0
    if [ $1 -le 0 ] 
    then
        echo 0
        return 0
    fi  

    sum=$(func $(expr $1 - 1))
    echo $(expr $sum + $1)
}

echo $(func 10)

输出结果:

55


函数内的局部变量

可以在函数内定义局部变量,作用范围仅在当前函数内

可以在递归函数中定义局部变量。

命令格式:

local 变量名=变量值

#! /bin/bash

func() {
    local name=yxc
    echo $name
}
func

echo $name

输出结果:

yxc

第一行为函数内的name变量,第二行为函数外调用name变量,会发现此时该变量不存在。

exit命令

作者:yxc
链接:https://www.acwing.com/blog/content/9731/

  • exit命令用来退出当前shell进程,并返回一个退出状态;使用$?可以接收这个退出状态。
  • exit命令可以接受一个整数值作为参数,代表退出状态。如果不指定,默认状态值是 0。
  • exit退出状态只能是一个介于 0~255 之间的整数,其中只有 0 表示成功,其它值都表示失败。

示例:

创建脚本test.sh,内容如下:

#! /bin/bash

if [ $# -ne 1 ]  # 如果传入参数个数等于1,则正常退出;否则非正常退出。
then
    echo "arguments not valid"
    exit 1
else
    echo "arguments valid"
    exit 0
fi


执行该脚本:

acs@9e0ebfcd82d7:~$ chmod +x test.sh 
acs@9e0ebfcd82d7:~$ ./test.sh acwing
>>arguments valid
acs@9e0ebfcd82d7:~$ echo $?  # 传入一个参数,则正常退出,exit code为0
>>0
acs@9e0ebfcd82d7:~$ ./test.sh 
>>arguments not valid
acs@9e0ebfcd82d7:~$ echo $?  # 传入参数个数不是1,则非正常退出,exit code为1
>>1

 重定向

文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)

命令说明
command > file将输出重定向到 file。
command < file将输入重定向到 file。
command >> file将输出以追加的方式重定向到 file。
n > file将文件描述符为 n 的文件重定向到 file。
n >> file将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m将输出文件 m 和 n 合并。
n <& m将输入文件 m 和 n 合并。
<< tag将开始标记 tag 和结束标记 tag 之间的内容作为输入。

输入和输出重定向
 

echo -e "Hello \c" > output.txt  # 将stdout重定向到output.txt中
echo "World" >> output.txt  # 将字符串追加到output.txt中

read str < output.txt  # 从output.txt中读取字符串

echo $str  # 输出结果:Hello World


同时重定向stdin和stdout
创建bash脚本:

#! /bin/bash

read a
read b

echo $(expr "$a" + "$b")

创建input.txt,里面的内容为:

3
4
执行命令:

acs@9e0ebfcd82d7:~$ chmod +x test.sh  # 添加可执行权限
acs@9e0ebfcd82d7:~$ ./test.sh < input.txt > output.txt  # 从input.txt中读取内容,将输出写入output.txt中
acs@9e0ebfcd82d7:~$ cat output.txt  # 查看output.txt中的内容
7

引入外部脚本

. filename  # 注意点和文件名之间有一个空格

或

source filename


创建test1.sh,内容为:

#! /bin/bash

name=knight02

然后创建test2.sh,内容为:

#! /bin/bash

source test1.sh # 或 . test1.sh

echo hello,"$name"  # 可以使用test1.sh中的变量

执行命令:

acs@9e0ebfcd82d7:~$ chmod +x test2.sh 
acs@9e0ebfcd82d7:~$ ./test2.sh 
hello,knight02

 

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泥烟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值