linux基础(五):bash、io与变量;数组;表达式;分支与循环

一、bash

1.1 shell就是一个bash程序

  • 解释器,启动器
  • 解释器:
    用户交互输入
    文本文件输入
  • 脚本本质:
    -#!/bin/bash
    -#!/usr/bin/python
  • 读取方式:
    bash file
  • 当前shell:source file.
  • 新建子shell:/bin/bash file 或者 ./file.sh (需要chmod +x file.sh)

命令行中的命令都可以放到一个文件中!
省的每次都得重新写大量的shell命令。

在当前shell执行脚本命令

type  source 
help  source
source  mysh.sh
./mysh.sh

[root@bk1 ~]# bash
[root@bk1 ~]# exit
exit
[root@bk1 ~]# echo $$  #打印当前bash的PID
1280

[root@bk1 ~]# bash 
[root@bk1 ~]# pstree 
init─┬─auditd───{auditd}
     ├─crond
     ├─master─┬─pickup
     │        └─qmgr
     ├─6*[mingetty]
     ├─rsyslogd───3*[{rsyslogd}]
     ├─sshd───sshd───bash───bash───pstree
     └─udevd───2*[udevd]
[root@bk1 ~]# exit
exit
[root@bk1 ~]# pstree
init─┬─auditd───{auditd}
     ├─crond
     ├─master─┬─pickup
     │        └─qmgr
     ├─6*[mingetty]
     ├─rsyslogd───3*[{rsyslogd}]
     ├─sshd───sshd───bash───pstree
     └─udevd───2*[udevd]

pstree -p #显示进程id

在这里插入图片描述

1.创建脚本到执行脚本

[root@bk1 ~]# vi mysh.sh
#!/bin/bash
ls  -l  /
echo  "hello world!"
添加执行权限
[root@bk1 ~]# chmod  +x  mysh.sh  #等价于a+x
[root@bk1 ~]# ./mysh.sh   #执行

2.bash mysh.sh #bash启动一个新进程,执行文件mysh.sh

[root@bk1 ~]# vi mysh1.sh
#!/bin/bash
ls  -l  /
echo  "hello world!"
echo  $$
pstree
[root@bk1 ~]# chmod  +x  mysh.sh
[root@bk1 ~]# ./mysh1.sh   #执行

3.让#!后跟的字符表示要启动的程序,该程序读取该文件执行。

[root@bk1 ~]# vi awk.sh
#!/bin/awk -f
{split($3, date, "-");
if (date[2] == "01") {name[$1]+=$5; 
if($2=="0"){role[$1]="Manager"}
else{role[$1]="Worker"}}}  
END{for(i in name){print i"\t"name[i]"\t"role[i]}}  

[root@bk1 ~]# chmod  +x  awk.sh
[root@bk1 ~]# ./awk.sh   awk.txt

定义函数:

myshellname () {
	command1
	command2
	command3
   ……
}
调用:
myshellname

1.2 总结:

bash是一个程序,shell是一个bash进程
bash是一个解释器,启动器
解释执行用户的输入指令,可以通过shell启动其他的进程

将要执行的命令放到一个文件中,
在文件的开头:
#!/bin/bash
#!/usr/bin/python
#!/bin/awk -f
用于指定该脚本由哪个程序负责解释执行

当前shell执行脚本:source   .
子进程执行:bash  mysh.sh或者./mysh.sh(需要该文件具有可执行权限)
	
定义函数:
funName() {
	各种命令

}
直接输入funName就可以执行了

1.3 io

重定向不是命令

  • 程序自身都有I/O
    0:标准输入
    1:标准输出
    2:错误输出
  • 控制程序I/O位置
  • 一切皆文件
    cd /proc/$$/fd
  • 程序是否处理I/O
  • 重定向绑定顺序:从左到右

ls / /hello 1> log.out
ls / /hello 1> log.out 2> log.err

1.3.0 数据重定向

标准输入(stdin):编号为0
标准输出(stdout):编号为1
标准错误输出(stderr):编号为2
1>:以覆盖的方法,将正确的数据输出到文件;  
1>>:以累加的方法,将正确的数据输出到文件;  
2>:以覆盖的方法,将错误输出的数据输出到文件;  
2>>:以累加的方法,将错误输出的数据输出到文件;  

ls  -l  >>  ok1.log
ls  -l  >  ok2.log
ls  hello 2>/root/err.log
ls  hello / 1>/root/log.log 2>/root/err.log
ls 1>/dev/null
ls  2>/tmp/err.log
既向控制台输出,也向文件写入
ls  -l  /  |  tee  ok2.log
tee命令,将输入分成两个输出

先输出错误输出,再输出正确输出

1.3.1 输出重定向: 重定向从左到右绑定

ls / /hello 2>&1 1> mylog.log
从左向右绑定,错误输出绑定到标准输出,此时标准输出是输出到控制台,然后才是标准输出重定向到文件。两个重定向的绑定没有关系。
注意&1符号的替换,直接将输出绑定到资源,而不是绑定到文件描述符
ls / /hello 1> mylog1.log 2>&1
先让标准输出重定向到文件,然后将错误输出绑定到标准输出,也就是左边绑定的文件。

第二种写法
标准输出和错误输出都重定向到文件
ls / /hello >& mylog2.log
ls / /hello &> mylog3.log

1.3.2 read

  • 将用户控制台的输入赋值给aaa变量
    read在不提供参数的时候,会将用户的输入存储在REPLY变量中
    [root@bk1 ~]# read
    abc
    [root@bk1 ~]# echo $REPLY
    abc

  • 如果提供了参数,则赋值给指定的参数
    [root@bk1 ~]# read aaa
    nihao
    [root@bk1 ~]# echo $aaa
    nihao

  • 将标准输入重定向到字符串,read读取后赋值给指定的变量:
    <<<将标准输入重定向到字符串
    [root@bk1 ~]# read aaa 0<<<“hello”
    [root@bk1 ~]# echo $aaa
    hello

1.3.3 了解

用在脚本中用于向控制台打印n行
[root@bk1 ~]# cat 0<<CATEOF

aaaaaa
bbbbbb
cccccccc
CATEOF
aaaaaa
bbbbbb
cccccccc

exec:使用指定的命令替换当前shell命令。
创建文件描述符:
以读写方式打开到www.baidu.com的80端口的tcp连接
exec 8<> /dev/tcp/www.baidu.com/80
echo -e “GET / HTTP/1.0\n” >& 8
cat <& 8
……
cd /proc/$$/fd #查看文件描述符
ll
echo -e “GET / HTTP/1.0\n” >&8 #重定向到8文件描述符
cat 0<& 8 从文件描述符8读取信息

1.4 bash变量

bash中变量的类型:
-本地变量
-局部变量
-位置变量
-特殊变量
-环境变量

1.4.1 本地变量

-当前shell所有
-生命周期跟当前shell一样

[root@bk1 ~]# a=99
[root@bk1 ~]# echo  $a
99

[root@bk1 ~]# myfunc() {
> myvar=99
> echo  $myvar
> }

[root@bk1 ~]# echo  $myvar    #访问不到
[root@bk1 ~]# myfunc  #调用函数
[root@bk1 ~]# echo  $myvar    #可以访问到
99

[root@bk1 ~]# abc=sxt
[root@bk1 ~]# echo $abc
[root@bk1 ~]# echo "$abcisnothere"
[root@bk1 ~]# echo "${abc}isnothere"

1.4.2 局部变量:

-只能用于函数
-local var=100

[root@bk1 ~]# myfunc(){
> local myvar=1001
> echo $myvar
> }
[root@bk1 ~]# myfunc
1001

[root@bk1 ~]# echo $myvar

1.4.3 位置变量:

$1,
${11}
-脚本
-函数

[root@node1 ~]# myfunc1(){
> echo $1
> }
[root@node1 ~]# myfunc1
[root@node1 ~]# myfunc1 hello
hello

[root@node1 ~]# myfunc2(){
> echo $4
> }
[root@node1 ~]# myfunc2 a b c
[root@node1 ~]# myfunc2 a b c d
d

[root@node1 ~]# myfunc3(){
> echo $13
> }
[root@node1 ~]# myfunc3 1 2 3 4 5 6 7 8 9 10 11 12 13
13
[root@node1 ~]# myfunc3 0 1 2 3 4 5 6 7 8 9 10 11 12 13
03
[root@node1 ~]# myfunc3 a 1 2 3 4 5 6 7 8 9 10 11 12 13
a3

[root@node1 ~]# myfunc3() {
> echo ${13}
> }
[root@node1 ~]# myfunc3 a 1 2 3 4 5 6 7 8 9 10 11 12 13
12
[root@node1 ~]# myfunc3 1 2 3 4 5 6 7 8 9 10 11 12 13
13

1.4.4 特殊变量

  • $#:位置参数个数

  • $*:参数列表,双引号引用为一个字符串 ./a a b c d e #“a b c d e”
    所有的参数作为一个字符串 5个参数作为一个字符串

  • $@:参数列表,双引号引用为单独的字符串 “a” “b” “c” “d” “e”
    所有的参数作为单个的字符串 5个参数作为五个字符串

  • $$:当前shell的PID:接收者

  • $BASHPID:真实的值

  • $?:上一个命令的退出状态
    0:成功
    其他:失败

[root@node1 ~]# func() {
> echo $*
> }
[root@node1 ~]# func 1 2 a b d "hell owlrld"
1 2 a b d hell owlrld

[root@node1 ~]# func() {
> echo $@
> }
[root@node1 ~]# func 1 2 a b d "hell owlrld"
1 2 a b d hell owlrld

$$和$BASHPID的区别
$$在哪个进程中执行命令,该值就是哪个进程的PID
$BASHPID就是当前进程的PID真实值
在这里插入图片描述
在这里插入图片描述
$$意味着该脚本文件下运行的进程ID。对于任何给定的脚本,在运行时,它会只有一个“主”进程ID。不管你有多少子shell调用,$$总是返回与脚本关联的第一个进程ID。 BASHPID会告诉你的bash的当前实例的进程ID,因此在子shell会跟调用它的“顶级”的bash不同。

二、数组:sxt=(a b c) #数组

Bash提供了一维数组变量。任何变量都可以作为一个数组;内建命令 declare 可以显式地定义数组。数组的大小没有上限,也没有限制在连续对成员引用和赋值时有什么要求。数组以整数为下标,从0开始

如果变量赋值时使用语法 name[subscript]=value,那么就会自动创建数组。 subscript 被当作一个算术表达式,结果必须是==大于等于 0 ==的值。

数组赋值可以使用复合赋值的方式,形式是== name=(value1 … valuen)==,这里每个 value 的形式都是[subscript]=string。如sxt[3]=d,string 必须出现。如果出现了可选的括号和下标,将为这个下标赋值,否则被 赋值的元素的下标是语句中上一次赋值的下标加一。下标从 0 开始。这个语法也被内建命令 declare 所接受。单独的数组元素可以用上面介绍的语法 name[subscript]=value 来赋值

数组的任何元素都可以用${name[subscript]}来引用。,如echo ${sxt[1]} # 取某一个元素,花括号是必须的,以避免和路径扩展冲突。如果subscript 是 @ 或是 *,它扩展为 name 的所有成员。这两种下标只有在双引号中才不同。在双引号中,${name[*]}扩展为一个词,由所有数组成员的值组成,用特殊变量 IFS 的 第 一 个 字 符 分 隔;${name[@]} 将 name 的每个成员扩展为一个词。如果数组没有成员,${name[@]} 扩展为空串。这种不同类似于特殊参数 * 和 @ 的扩展 (参见上面的 Special Parameters 段落)。${#name[subscript]} 扩展为${name[subscript]}的长度。如果 subscript 是 * 或者是 @,扩展结果是数组中元素的个数。引用没有下标数组变量等价于引用元素 0。

内建命令 unset 用于销毁数组。unset name[subscript] 将销毁下标是 subscript 的 元 素 。 unset name, 这里 name 是一个数组,或者 unset name[subscript], 这里 subscript 是 * 或者是 @,将销毁整个数组。

内建命令 declare, local, 和 readonly 都能接受 -a 选项,从而指定一个数组。内建命令 read 可 以接受 -a 选项,从标准输入读入一列词来为数组赋值。内建命令 set 和 declare 使用一种可以重用为输入的格式来显示数组元素。

[root@bk1 ~]# sxt=(a  b  c)  #数组
[root@bk1 ~]# echo  $sxt  #默认取第一个元素
a  
[root@bk1 ~]# echo  ${sxt[1]} # 取某一个元素
b
[root@bk1 ~]# echo  ${sxt[*]}
a b c
[root@bk1 ~]# echo  ${sxt[@]}
a b c
[root@bk1 ~]# echo  $sxt[1] # 错误的写法

管道
[root@bk1 ~]# a=9
[root@bk1 ~]# echo  $a
9
[root@bk1 ~]# b=22  | echo  ok
ok
[root@bk1 ~]# echo  $b      #访问不到子进程的数据

父进程修改值不会影响子进程的变化,子进程值的修改也不会影响父进程的值。
fork子进程,写时复制 copy on write
管道两边的命令在当前shell的两个子进程中执行。

[root@bk1 ~]# vi mysh.sh
#!/bin/bash
echo  $a
echo  "---------"

[root@bk1 ~]# chmod  +x  mysh.sh
[root@bk1 ~]# ./mysh.sh
[root@bk1 ~]# export  a    #将a导出为环境变量,子进程就可以访问了
[root@bk1 ~]# ./mysh.sh    #可以打印出a的值

子进程睡20s,在此期间修改环境变量的值,查看export是导出还是共享
[root@bk1 ~]# vi mysh.sh
echo  $a
echo  "----------"
sleep  20
echo  $a

[root@bk1 ~]# a=110   #主进程设置值
[root@bk1 ~]# export a #导出变量
[root@bk1 ~]# ./mysh.sh   &    #后台执行子进程
[root@bk1 ~]# a=220  #主进程修改值
[root@bk1 ~]# export a #导出变量
子进程不能打印220,而是打印110

三、引用

  • 双引号:弱引用,参数扩展
  • 单引号:强引用,不可嵌套
  • 花括号扩展不能被引用
  • 命令执行前删除引用
[root@node1 ~]# a=99
[root@node1 ~]# echo "$a"   # 99双引号引用,弱引用  
[root@node1 ~]# echo "\"$a\""   #"99"
[root@node1 ~]# echo '$a'    #$a  单引号引用,强引用

花括号扩展,创建adir,bdir,cdir三个目录
mkdir  ./{a,b,c}dir

花括号扩展,拷贝/etc/profile以及/etc/init.d/network到当前目录
cp  /etc/{profile,init.d/network}  ./  #把etc/profile和etc/init.d/network拷贝到当前目录
  • 命令替换:
    命令替换允许我们将shell命令的输出赋值给变量。它是脚本编程中的一个主要部分。
    命令替换会创建子shell进程来运行相应的命令。子shell是由运行该脚本的shell所创建出来的一个独立的子进程。由该子进程执行的命令无法使用脚本中所创建的变量。

  • 反引号:ls -l

  • $(ls -l /) 可以嵌套
    反引号提升扩展优先级,先执行反引号的内容,再执行其他的。

错误
[root@node1 ~]# myvar=echo "hello"
[root@node1 ~]# myvar=`echo "hello"`

命令替换的嵌套
[root@node1 ~]# myvar=$(echo $(echo "hello world"))
[root@node1 ~]# myvar=$(echo "hello world")
[root@node1 ~]# myvar="hello world"

四、逻辑判断:

退出状态:echo $?

  • command1 && command2
    如果command1退出状态是0,则执行command2
  • command1 || command2
    如果command1的退出状态不是0,则执行command2
[root@node1 ~]# [ -d /hello ]
[root@node1 ~]# test -d /hello || echo "文件夹/hello不存在"
[root@node1 ~]# test -d /bin && echo "文件夹/bin存在"
[root@node1 ~]# test -f profile && rm -f profile && touch profile

[root@node1 ~]# ls / && echo ok
[root@node1 ~]# ls / || echo ok

五、表达式

5.1 算术表达式:

  • let 算数运算表达式
    let C=$A+$B !!!

  • $[算术表达式]
    C=$[$A+$B]

  • $((算术表达式))!!!
    C=$((A+B))

  • expr算术表达式
    表达式中各操作数及运算符之间要有空格,同时要使用命令引用
    C=expr $A + $B

[root@node1 ~]# a=1
[root@node1 ~]# b=2
[root@node1 ~]# let c=$a+$b
[root@node1 ~]# echo $c
[root@node1 ~]# d=$((a+b))
[root@node1 ~]# echo $d

[root@node1 ~]# ((a++))
[root@node1 ~]# echo  $a
前置++表示先自身++之后再参与计算
后置++表示先计算,再自身++
[root@node1 ~]# c=$((a+b)) && echo $c
4
 [root@node1 ~]# c=$((a--+b)) && echo $c
4
[root@node1 ~]# c=$((a--+b)) && echo $c
3
[root@node1 ~]# echo $a 
0
[root@node1 ~]# echo $b
2
[root@node1 ~]# c=$((--a+b)) && echo $c
1
[root@node1 ~]# c=$((a--+b)) && echo $c
1
[root@node1 ~]# echo $a $b
-2 2

[root@node1 shdemo]# e=$((a*b))  ?????

5.2 条件表达式

  • [ 表达式 ]
  • test 表达式
  • [[ 表达式 ]]
    help test
[root@node1 shdemo]# test  3  -gt  2
[root@node1 shdemo]# echo  $?
[root@node1 shdemo]# test  3  -gt  8
[root@node1 shdemo]# echo  $?
[root@node1 shdemo]# test  3  -gt  2  &&  echo  ok
等价于
[root@node1 shdemo]# [  3  -gt  2  ]  &&  echo  ok

[root@node1 shdemo]# test  3  -gt  8  &&  echo  ok
等价于
[root@node1 shdemo]# [  3  -gt  8  ]  &&  echo  ok

练习题:
-添加用户 useradd <username>
-用户密码同用户名 passwd --stdin <username> 0<<<"username"
-静默运行脚本
-避免捕获用户接口
-程序自定义输出 echo "user added!"

[root@bk1 ~]# vi mysh.sh
#! /bin/bash
useradd  $1   接收第一个参数作为用户名

使用管道将用户输入的第一个参数传递给passwd,passwd命令指定选项--stdin就不会捕获用户接口了,
同时将passwd的标准输出重定向到/dev/null(数据黑洞)
passwd  --stdin  $1  >&  /dev/null
echo  "user  added!"

问题:
如果用户没有输入参数或参数个数不对怎么办?

[root@bk1 ~]# vi mysh.sh
#!/bin/bash
[  !  $#  -eq  1  ]  &&  echo  "Usage: ./$0 arg1"  &&  exit  2
useradd  $1
passwd  --stdin  $1  >&  /dev/null
echo "user  added"

[root@bk1 ~]# bash  mysh.sh  hello  				执行
[root@bk1 ~]# id hello  							查看hello用户是否添加成功
[root@bk1 ~]# userdel  -rf  hello  如果添加成功,就删除该用户,以继续后面的测试

问题:
如果要添加的用户已经存在怎么办?

[root@bk1 ~]# vi adduser.sh
#!/bin/bash
[  !  $#  -eq  1  ]  &&  echo  "Usage: ./$0 arg1"  &&  exit  2
#使用id命令检查,看返回值是否为0,如果是表示用户存在,提示并退出
id  $1  &> /dev/null  &&  echo "user  exists!"  &&  exit  3
useradd  $1
passwd  --stdin  $1  >&  /dev/null
echo "user  added"

[root@bk1 ~]# chmod 700 adduser.sh
[root@bk1 ~]# ./adduser.sh  user1

六、分支与循环

6.1 if

  • 单分支结构
    if  [  条件判断  ]
    then //命令
    fi # 将if反过来写,就成为fi  结束if语句 

  • 双分支结构
    if  [  条件1  ];then 
     条件1成立执行,指令集1 
    else 
    条件1不成执行指令集2; 
    fi

  • 多分支结构
    if  [  条件1  ];then 
     条件1成立,执行指令集1
    elif  [  条件2  ];then 
     条件2成立,执行指令集2 
    else 
    条件都不成立,执行指令集3
    fi 

使用[ 条件表达式]命令判断
if [ 3 -gt 2 ]; then
echo ok
fi

if [ 3 -gt 8 ];
then echo ok;
fi

[root@node1 ~]# cat sh01.sh 
#!/bin/bash
a=20
if [ $a -gt $1 ];
then
  echo "你输入的数字太小"
elif [ $a -eq $1 ];
then
  echo "恭喜哈,数字相等"
else
  echo "你输入的数字太大"
fi 

6.2 case

case  $变量名称  in 
“值1") 
 程序段1 
 ;;  
“值2") 
 程序段2  
 ;; 
*) 
 exit  1 
 ;; 
esac 

案例:判断用户输入的是哪个数,1-7显示输入的数字,1显示 Mon,2 :Tue,3:Wed,4:Thu,5:Fir,6-7:weekend,其它值的时候,提示:please input [1,7],该如何实现?

[root@node1 ~]# cat sh02.sh 
#!/bin/bash
read -p "please input a number[1,7]:" num
case $num in
1)
    echo "Mon"
;;
2)
    echo "Tue"
;;
3)
    echo "Wed"
;;
4)
    echo "Thu"
;;
5)
    echo "Fir"
;;
[6-7])
    echo "weekend"
;;
*)
    echo "please input [1,7]"
;;

esac
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值