Linux学习05之Shell编程

1. 脚本编程-bash

大纲图图1
大纲图图2
bash是解释器、启动器
解释器

  • 用户交互输入
  • 文本文件输入

脚本的本质启动子进程去读取程序,指定了启动命令的话会执行指定的,如果没有指定,会默认启动一个bash,即使你没有指定bash

  • #! /bin/bash 表示此程序会启动一个bash来执行程序
  • #! /usr/bin/python 启动一个指定的脚本来执行程序

启动文件的三种方式

  1. 如果是新建的脚本文件例如sh01.sh可以采用source sh01.sh或者. sh01.sh表示在当前bash执行
  2. 如果是采用bash sh01.sh,表示在子bash中执行
  3. 让文件具有可执行的权限,chmod +x sh01.sh,此时执行文件可以采用./sh01.sh

示例:
在这里插入图片描述
使用pstree可以打印出进程树,可以看出bash是子进程在执行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
sh01.sh脚本内容分析:

#! /bin/bash          // 头部
ls /                  // 列出所有文件
echo "caoduanxi"      // 打印内容
echo $$               // 输出当前进程号
pstree                // 打印出进程树

执行过程分析启动文件时,会读取/bin/bash,此时首先启动一个子进程,如果没有指定/bin/bash,会默认开启一个bash,可以尝试在xshell中敲下,你会现如此。然后才开始执行文件,此时遇到#!注释掉,执行ls,然后echo,最后打印出进程树。

2. 脚本编程-文本流&重定向

内容图图3
内容图图4

文件的重定向,在linux中有一句话叫做一切皆文件,无论是什么外部的文件或者设备在linux中都是以文件的形式展现出来。
echo $$ 可以查看当前进程号
cd /proc/$$/fd 可以查看到当前进程的文件描述符
在这里插入图片描述
可以使用ps -ef | grep 1325查询进程,然后根据父进程,可以看到命令执行是通过root@pts/0.
在这里插入图片描述
可以看到每一个进程执行命令都是相当于客户端向服务端发送一个请求,请求重定向到某些地方执行,服务端相当于一个虚拟中断,这种方式叫做重定向。上述文件描述符是有含义的:

  1. 0表示标准输入
  2. 1表示标准输出
  3. 2表示错误输出

具体的重定向命令以及流程:

重定向命令 含义
ls / /a 1>/bbb 展示根目录与/a目录下文件到bbb文件中
ls /b 1>>/bbb 表示在bbb中追加/b目录下的文件展示
ls /aaa 2>/bbb 提示/aaa不存在,此时会有错误信息定向输出到bbb文件中

上述多个文件重定向需要注意,如果不是同级的目录,则按照文件深度来排序,如果是同级目录,则按照字典序排列。

ls / /aaa 2>&1 1>err01.out这句命令表示2先定向到1,此时1定向到控制台,此时输出错误信息,然后1订到err01.out文件,所以先输出错误信息,在文件中获取到根目录下文件。
在这里插入图片描述
要注意绑定的顺序是从左到右。
ls >& fff 或者 ls &> fff这是一种特殊写法也是重定向,此时输出当前目录的展示文件到fff文件。


输入重定向,使用read命令来实现输入的重定向。

[root@node01 a]# read aaa<<< "hello caoduanxi"      // "<<<" 字符重定向
[root@node01 a]# echo $aaa                          // 读取内容
hello caoduanxi
[root@node01 a]# read bbb<<AABB                     // “<< 表示指定一个结束符
> adsfasd											// 从结束符中读取第一行
> aaaa                                              // read对换行符敏感
> aaaa												// 只会读取出第一行
> AABB                                              // 结束符需要相同
[root@node01 a]# echo $bbb
adsfasd
[root@node01 a]# read ccc<awk.txt                   // “<”从文件中读取数据
[root@node01 a]# echo $ccc
Tom 0 2012-12-11 car 3000

内容图图5

实例:获取百度网站内容,然后输出到指定文件

exec 8<> /dev/tcp/www.baidu.com/80 表示执行输出输出到8
在这里插入图片描述
echo -e "GET / HTTP/1.0\n" 1>&8 指定请求协议 重定向到8中
cat 0<&8 表示查看从8输出到0中的文件内容,此时可以看见百度的html文件

3. Shell-变量学习

说明,在shell中变量分为局部变量和本地变量,局部变量无论方法外还是方法内都是可以访问到,但是局部变量只能在方法内本地能够访问到,需要加local修饰符来修饰。而且每一个局部变量的操作都只能在当前bash中有效,一旦开启或者退出一个bash,然后重新想使用刚刚的bash中的变量是不成功的。
示例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
内容图图6

4. Shell引用的学习

在linux中使用数组:注意bash对于逗号不敏感,对于空格或者换行符敏感,注意在数组或者其他地方需要获取参数都需要使用{}括起来,否则结果会直接将你所需要的参数当做字符串拼接起来。

[root@node01 a]# a=(1 2 3)
[root@node01 a]# echo ${a[1]}
2
[root@node01 a]# echo ${a[0]}
1
[root@node01 a]# echo ${a[2]}
3

使用echo $?可以判断bash命令是否成功,成功返回0,不成功返回非0,结果只有0与非0之分。

在linux中管道对于变量的使用以及文件对于变量的使用:

// 管道中在当前bash中开启的子bash是会继承父bash中的变量
[root@node01 a]# a=9
[root@node01 a]# echo $a
9
[root@node01 a]# a=22 | echo $a
9
---------------------------------
// 文件中要想获取父bash中的变量需要使用export导入,否则无法获取到变量的值
[root@node01 a]# vi sh03.sh 
[root@node01 a]# ./sh03.sh 

1383
[root@node01 a]# d=1234
[root@node01 a]# ./sh03.sh 

1384
[root@node01 a]# export d=1234
[root@node01 a]# ./sh03.sh 
1234
1385

在这里插入图片描述
内容图图7
内容图图8

变量的生命周期都是跟随着bash走的,bash如果执行完毕,此bash中的变量也就消亡了。如果想在bash中修改参数的话,需要允许父bash在后台运行./sh05.sh &在末尾追加一个&符号即可,此时可以在中途改变父bash中的某个变量的值。

[root@node01 a]# cat sh04.sh 
echo $a
echo "-------"
sleep 10
echo $a

中途有十秒的时间可以执行更改a变量,当更改完毕,再获取的话,此时a就已经变化了。

强应用与弱引用由下可以知道双引号是弱引用,引用的内容不会被覆盖,单引号是强引用,引用的内容会被覆盖。

[root@node01 a]# c="caoduanxi"
[root@node01 a]# echo "$c"
caoduanxi
[root@node01 a]# echo '$c'
$c

命令提前执行与替换:使用反引号可以使得语句中的命令被先执行,使用$可以使得其中命令被替换。

[root@node01 a]# echo "echo "caoduanxi""
echo caoduanxi
[root@node01 a]# echo "`echo "caoduanxi"`"
caoduanxi
-------------------------------------------------
[root@node01 a]# abc=$(echo $(echo "caoduanxi"))
[root@node01 a]# echo $abc
caoduanxi

在linux中,使用echo $?可以查看命令是否执行成功,支持与逻辑与或逻辑操作符。/aabb目录是不存在的

[root@node01 a]# ls /aabb || ls /a
ls: cannot access /aabb: No such file or directory
awk.sh   bbdir  cccc  grep.txt  inittab     profile  sh02.sh  
sh04.sh  test
[root@node01 a]# echo $?
0
[root@node01 a]# ls /aabb && ls /a
ls: cannot access /aabb: No such file or directory
[root@node01 a]# echo $?
2

内容图图9

5. Shell表达式的学习

内容图图10
内容图图11

算数表达式的两种常用形式:let

[root@node01 a]# let c=$a+$b    // 第一种计算形式
[root@node01 a]# echo $c
3
[root@node01 a]# d=$(($a+$b))   // 第二种计算形式
[root@node01 a]# echo $d
3
[root@node01 a]# f=$((a+b))     // 简化的第二种计算形式
[root@node01 a]# echo $f
3
[root@node01 a]# ((a++))        // 自增不用赋值不用写$
[root@node01 a]# echo $a
2

条件表达式学习:test[ ],判断逻辑都是根据echo $?来的,0代表true,非0代表false,两者都是内部标签,可以直接使用help test 或者 help [查看具体使用。

[root@node01 a]# test -a /aaaabb    // 使用test
[root@node01 a]# echo $?
1
[root@node01 a]# test -a /         
[root@node01 a]# echo $?
0
[root@node01 a]# [ 3 -gt 3 ]
[root@node01 a]# echo $?
1
[root@node01 a]# [ 3 -gt 1 ]
[root@node01 a]# echo $?
0

内容图图13

练习编写一个脚本:

要求:
实现添加用户
用户密码同用户名
静默运行脚本
避免捕获用户接口
程序自定义输出

具体实现:创建一个脚本文件sh01.sh,内容如下即可

#!/bin/bash
useradd $1
echo $1 | passwd --stdin $1
echo "user add success"

上述脚本存在的问题:

  • 参数个数未限制
  • 脚本没有静默运行
  • 参数没有做重复校验
  • 执行权限没有校验

对脚本做一个命令的解释:

// 头文件,不写会默认添加
#! /bin/bash
// 做校验,如果参数个数为1则不执行,直接下一步,不为1的话输出参数错误,退出
[ ! $# -eq 1 ] && echo "args error" && exit 2
// id $1主要是检验是否重复,如果有重复,提示信息输出到/dev/null,这是一个文件“黑洞”
// 输出用户已经存在,退出3
id $1 >&/dev/null && echo "user is exists" && exit 3
// 前面的重定向主要是为了将权限的提示输出到其中,然后执行密码输出与确认信息输出到/dev/nul
// 然后提示成功,退出
useradd $1 >&/dev/null && echo $1 | passwd --stdin $1 >&/dev/null && echo "user add success" && exit 4
// 如果都没成功,输出最后的消息并退出
echo "I don't know, user add failed!" && exit 5

6. 流程控制

流程控制包括if/else/elifforwhile

[root@node01 home]# if [ 3 -ge 4 ];then echo "3";else echo "4";fi
4
[root@node01 home]# if [ 3 -ge 4 ];then ech0 "3";elif [ 3 -le 8 ];then echo "8";fi
8
-----------------------------------
[root@node01 home]# for((i=0;i<5;i++));do echo $i;done
0
1
2
3
4
-----------------------------------
[root@node01 home]# for i in 1 2 3 4 5;do echo $i;done // 结果同上
------------------------------------
[root@node01 home]# a=1
[root@node01 home]# while test $a -le 5;do echo $a;((a++));done // 结果同上
-----------------------------------
// 使用序列,结果如上,注意结果需要提前运行
[root@node01 home]# for i in `seq 5`;do echo $i;done 

内容图图14

练习:
用户给定路径
输出文件大小最大的文件
递归子目录

#! /bin/bash
oldIFS=$IFS   // 使用换行符
IFS=$'\n'
// 遍历所有文件,按照数字逆序排列
for i in `du -a $1 | sort -nr`;do
// 定义变量filename,利用awk文本处理工具打印出第二行
	filename=`echo $i | awk '{print $2}'`
	// 如果是文件,输出文件名,break
	if [ -f $filename ];then 
	echo $filename
	break;
	fi
done
// 还原原来的配置
IFS=$oldIFS

内容图图15

练习
循环遍历文件每一行:流程控制
定义一个计数器
打印num正好是文件行数

实现:

#! /bin/bash
num=0
oldIFS=$IFS
IFS=$'\n'
// cat $1 获取文件中的,然后读取每一行
for i in `cat $1`;
	do echo $i;
	((num++));
done
echo "num:$num"
IFS=$oldIFS
echo "-------------------------"
num=0
// 利用管道获取file.txt中所有的行数
lines=`cat file.txt | wc -l`
for((i=1;i<=lines;i++));
	// 获取到前几行数据,然后获取到最后一行
	do line=`head -$i file.txt | tail -1`
	echo $line;
	((num++));
done 
echo "num:$num"
echo "-------------------------"
num=0
// 读取行
while read line;
	// 输出行
	do echo $line;
	((num++));
// 从文件中读取文件
done <file.txt
echo "num:$num"
echo "-------------------------"
num=0
// 获取文件,由于管道会产生一个子bash,子bash会继承父bash的数据所以可以实现
cat file.txt | while read line;
        do echo $line;
        ((num++));
done
echo "num:$num"

7. 小结

这一节学习了bash与shell,bash是一个进程,shell是一个壳,需要学会bash之后再来看shell,shell中的变量,shell中的引用,shell中的重定向,相关的命令$#/$*/$?等等,以及强引用与弱引用,管道与export之间shell数据与变量的区别,流程控制,利用脚本来实现用户的添加密码的设置,实现数据获取的自动化。总的来说linux的基础在这里学完了,还需要好好的复习前面的知识,这样才可以达到高效率的开发。大数据学习加油~~

发布了74 篇原创文章 · 获赞 12 · 访问量 8220

猜你喜欢

转载自blog.csdn.net/cao1315020626/article/details/100087095