前面我们熟悉了shell脚本的一些基础语法,现在我们继续深入。
Shell管道
语法结构 命令1 | 命令2 |... | 命令n
把命令1的输出当作命令2输入,依次类推
案例,打印当前文件夹内以“test”命令开头的项目:
ls | grep "test"
“ls” :打印当前文件夹内所有文件及文件夹
“grep”:过滤命令,这里有一篇专门讲解grep命令的文章 https://blog.csdn.net/bmengmeng/article/details/91979846
"expr"命令深入研究
expr支持 + - * / 等多种算数运算,其常见的方式有:
方式一:
#!/bin/bash
a=100
b=200
val=`expr $a + $b`
echo "val值:$val"
执行结果
val值:300
方式二:
#!/bin/bash
a=100
b=200
val=$(expr $a + $b)
echo "val值:$val"
执行结果
val值:300
方式三:
#!/bin/bash
a=100
b=200
val=$[ $a + $b ]
echo "val值:$val"
执行结果
val值:300
综上所述,比较直观的使用方式是第三种,也是我们常用的方式,这种方式为expr隐式调用。
至此我们了解了shell的整数算数运算,但你会发现无法进行浮点数的运算,接下来我们继续。
要了解浮点数运算,首先要了解“bc”这个命令。
bc(Binary Calculator)计算器,用于实现任意精度计算(往往是高精度计算)。
首先我们在终端使用一下,直接在终端输入命令:bc,回车后你将看到bc命令的版本号及一些相关信息,同时等待你的输入,在这个控制面板你可以直接输入运算表达式,例如输入“1+1”然后回车:
退出计算器模式的命令为:quit,当然你也可以使用通用的退出方式,ctrl+c,不过你发现这次不灵了🙅♂️,哈哈。
刚才我用在终端使用了一下bc命令,现在我们要将它运用到脚本中。
语法结构:val=$(echo "算数表达式" | bc)
代码示例:
#!/bin/bash
val=$(echo " 1.56555 * 100 " | bc)
echo "val值:$val"
这里运用了管道,将echo命令的输出当成bc命令的输入。
得到结果后,你会发现它会自动给你保留参与运算的数值中精度最大的那个值的精度,例如本例中1.56555为精度最大的,所以结果为“156.55500”,保留了同样的5位小数。
但以上这种方式只适合较为简单的运算,接下来我们来一个复杂的运算。
语法格式:val=$(bc << FG
表达式
FG
)
脚本代码
#!/bin/bash
val1=1.314
val2=0.618
val3=100
val4=100
val=$(bc << FG
a = ($val1 *$val2)
b = ($val3 *$val4)
a * b
FG
)
echo "val结果:$val"
这种方式虽然可以结算出结果,但结果的精度存在偏差,因为该例中最大精度点位小数点后三位,所以计算结果最大精度也是小数点后三位,但实际结果的精度为小数段后6位,所以这种方式平时少用,用则慎之。
刚刚那个例子用到 “<<” 这个符号,这是个什么东西呢,接下来就讲解shell脚本的重定向功能。
Shell重定向
重定向目的:就是操作文件输入和输出
输出重定向,语法 command1 > file1或者 command1 >> file1,前者位替换,后者位追加
案例,终端输入:
ls > 文件列表.txt
将原本输出在终端的内容重定向输出到文件列表.txt这个文件中去。
输入重定向,语法 command1 < file1
接上个例子,我们需要统计 文件列表.txt 文件的行数,执行以下命令:
wc -l 文件列表.txt
也可以将输入重定向到 文件列表.txt 文件:
wc -l < 文件列表.txt
注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。
重定向深入讲解
一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
如果希望 stderr(标准错误) 重定向到 file,可以这样写:
command 2 > file
追加的话就这样写
command 2 >> file
如果希望 stderr(标准错误)和输出都 重定向到 file,可以这样写:
command > file 2>&1
追加的话就这样写
command >> file 2>&1
如果希望对 stdin 和 stdout 都重定向,可以这样写:
command < file1 >file2
上面的代码意思是:command 命令将 stdin(输入) 重定向到 file1,将 stdout(输出) 重定向到 file2
接下来几个案例
案例一,在当前脚本文件内读取其他文件内容:
#!/bin/bash
#0代表标准输入/读取数据,这里的意思是读取 文件列表.txt 的内容
exec 0< 文件列表.txt
#循环遍历读取
count=1
#一旦设置了输入重定向,那么read命令自动链接文件输入内容
while read line
do
echo "当前行数:$count 读取内容:$line"
count=$[ $count + 1 ]
done
exec <file 将file中的内容作为exec的标准输入
exec >file 将file中的内容作为标准输出
案例二,将正常内容输出到 内容.txt 文件,将错误信息输出到 error.txt 文件(内容.txt和error.txt可以不用创建,系统会自动识别,若没有没有就帮你创建一个),这里我们创建一个content.txt,并在里面输入内容:
你好
helloworld
在终端输入:
ls -a content.txt fileA.txt 2> error.txt 1> 内容.txt
执行结果:终端没有任何显示,因为标准输出已经重定向到了内容.txt里面,错误信息重定向到了error.txt里面
临时重定向
在实际项目中我们可能遇到这种情况,我要将fileA中标记的错误信息,全部保存到error.txt中,通过STDERR(标准错误)重定向到error.txt里面,代码如下:
#!/bin/bash
#2表示标准错误,以下这种方式将这句话定义为错误信息
echo "Helloworld" >&2
echo "你好" >&2
echo "完了"
终端执行:
./fileA.sh 2> error.txt
结果,终端显示“完了”,而被定义为错误信息的重定向到了error.txt里面。
永久重定向
在实际项目中我们可能遇到这种情况,我要将fileA中的输出信息保存到fileB中,将出现的错误信息保存到error.txt中,通过STDOUT(标准输出)重定向到fileB里面,再通过STDERR(标准错误)重定向到error.txt里面,代码如下:
#!/bin/bash
#1表示标准输出,将输出重定向到test.txt
exec 1> test.txt
echo "Hello world"
echo "你好"
exec 2> error.txt
echo "我报错了" >&2
自定义重定向
0、1、2系统默认提供的,那么我们自己可不可以自定义描述符呢?答案是可以的!示例如下:
#!/bin/bash
#exec命令:自定义文件描述符
#重定向输出
exec 40>> fileContent.sh
echo "Helloworld"
echo "你好" >&40
输出被重定向后是否可以恢复呢?答案也是肯定的!,示例如下:
#!/bin/bash
#exec命令:自定义文件描述符
#自定义一个临时的描述符3,重定向到1
exec 3>&1
#正常输出重定向到fileContent.sh
exec1> fileContent.sh
echo "Hello world"
echo "你好"
#将1还原为3 解除输出重定向
exec 1>&3
#解除之后echo就能正常在终端输出日志了
echo "正常了"
创建可读可写的描述符
在实际项目中我们可能对一个文件既要读取又要写入,那没有这么实现:
文件内容->fileContent.sh
你好
hello
脚本代码->fileA.sh
#!/bin/bash
exec 3<>fileContent.sh
#从fileContent.sh文件里面读取内容
read line <&3
echo "Read:$line"
#写入内容
echo "I have a Dream" >&3
注意:替换了第二行?因为我们文件指针读取完了第一行,文件指针指向了第二行,所以第二行之后所有的内容被替换
/dev/null 文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
command > /dev/null
如果你希望清空某个文件,你可以:
cat /dev/null >fileContent.sh