第十期 bash 学习笔记

操作系统简史

OS时代

  • 1973 贝尔实验室Unix AT&T Unix
  • 1982 BSD Unix
  • 1991 SUN Solarls

PC时代

  • 1975 乔布斯Apple
  • 1980 比尔盖茨DOS

GUI时代

  • 1979 乔布斯Mac
  • 1990 比尔盖茨Windows
  • 1994 Liunx

移动OS时代

  • 2005 Google收购Android
  • 2005 乔布斯IOS

shell

  • 1977 sh
  • 1989 bash

bash变量类型

# 整型
a=1  
# 字符串
b="hello word"    
# 布尔
c=true 
# 数组
array=(a b c) 
# 函数
foo() { echo hello world } 

= 左右两边不要有空格

变量的使用

# 定义变量a
a=1  
# 定义变量b
b="hello wrod"

# 输出变量a的值  echo相当于python的print() $引用变量
echo $a  
输出:1

# 输出变量b的值
echo $b
输出:hello wrod

# 双引号可以加变量,单引号加变量话当成字符串
c="b=$b"
输出:b=hello wrod
c='b=$b'
输出:b=$b

# shell里面输出没有定义过的变量也不会报错,只是显示空
echo $bbb

# 字符串拼接变量
echo ${b} hello shell
输出:hello wrod hello shell

浮点数的计算

bash默认不支持浮点数的除法,如果要使用可以使用下面的方式

# 计算浮点数
awk 'BEGIN{printf 1/3}'
输出:0.333333

# 格式化显示
awk 'BEGIN{printf("%.2f\\n", 1/3)}'
输出:0.33

预定义变量

echo $PWD  # 当前路径和小写pwd一样
echo $USER # 当前用户
echo $HOME # 当前用户的家目录
echo $PATH  # 当前的环境变量

which 查看当前项目安装的路径

# 查看python安装的路径
which python 
# which只能找在环境变量存在的路径

export 把项目加入环境变量中

export PATH=$PATH:要加入环境变量的路径
# 实例,将home加入环境变量中
export PATH=$PATH:/home
source 通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录

数组变量

# 定义数组
array=(1 2 3 4 5)

# 引用数组,并输出数组所有值
echo ${array[@]} 
echo ${array[*]} 

# 取出数组第三个值
echo ${array[2]}

# 取出数组最后一个值
echo ${array[-1]}

# 将命令赋值给数组
array=(`ls`)
echo array  # 你在哪一层目录将ls赋值给array的,就会显示那一层ls下的所有文件

数组使用*和@的区别

# 加上双引号时
 array=(1 2 3);for line in "${array[@]}";do echo $line; done
输出:
1
2
3
array=(1 2 3);for line in "${array[*]}";do echo $line; done
输出:
1 2 3

# 不加上双引号时
array=(1 2 3);for line in ${array[@]};do echo $line; done
输出:
1
2
3
array=(1 2 3);for line in ${array[*]};do echo $line; done
输出:
1
2
3
  • 由此可见当加上双引号后echo \${array[@]} 是以数组的方式一次输出一个值,echo \${array[\*]}是以字符串的方式拼接成一个值输出。
  • 不加上双引号两者是没有区别的

特殊符号

  • " "双引号用于括起一段字符串值,支持$var形式的变量替换
  • ' '单引号也表示其内容是字符串值,不支持转义
  • \ \\ 反引号的作用就是将反引号里面的内容当做是命令在执行。必须是shell真的存在的命令
a=`ls`
echo a  # 相当于是ls命令了
# 将ls命令赋值给a 
  • $(ls) 表示执行ls后的结果。与\ \\类似。不过可以嵌套

  • 反引号和\$()的区别:
    1.容易和单引号混淆;
    2.在多层嵌套使用时必须额外的跳脱(\\`)处理,而使用$(ls)就没有这样的问题。

  • \\ 转义符

echo  -e "a\\nbb"  
输出:
  a
  bb

参数
# -e 开启转义
# \\ 转译符
# \\n 换行
# \\b 删除它前面一个字符
# \\a 发出警告声

echo -e "a\\bb"
输出:b
  • $(()) 对变量进行操作,+ - * / % & | !等
a=1
b=2
echo $((a+b))
输出:3

echo $((2+3))
输出:5
  • (()) 是整数扩展,把里面的变量当做整数去处理
a=1
b=2

(($a>$b)); echo $?  # 表示1是否大于2 返回的是false
输出:1

(($a<$b)); echo $? # 表示1是否小于2 返回的是true
输出:0
说明:在shell中0表示true,其他非0代表false

((a=a+b)); echo $a
输出:3

((a++)); echo $a
输出:2

$(()) 和 (())的区别
$是变量的标志
(())是运算的标志
$((运算))代表运算结果
  • seq 生成一个序列
array=(`seq 1 10`)
echo array
输出:1 2 3 4 5 6 7 8 9 10
  • ({1...10}) 等价于 seq 1 10,表示1到10
  • ${ } 用来作变量替换用的,一般情况下,\$var 与 \${var} 并没有啥不一样。但是用 \${ } 会比较精确的界定变量名称的范围。
  • $?命令执行返回都结果,是true 还是 false

字符串操作

切片

s="hello shell"
echo ${s:6} # 下标第6个位置开始取字符串hello shell ,下标从0开始哦
输出:shell 

切片取字符串长度

s="hello shell"
echo ${s:6:3}  # 表示从第6个位置开始取长度3个字符
输出:she

计算字符串长度

s="hello shell"
echo ${#s}
输出:11

掐头去尾 (非贪婪模式,只取第一个匹配到的字符)

s="hello world"

echo ${s#hello}  # 掐头 把hello 去掉 使用 #
输出:world

echo ${s#*w}  #  *表示匹配任意字符,只要碰到w前面的所有字符都去掉(包括w)
输出:orld

echo ${s%world} # 去尾,使用 %
输出:hello

echo ${s%w*} # 去尾,只要碰到w后面的所有字符都去掉(包括w)
输出:hello

掐头去尾(贪婪模式,一直匹配到最后一个对应的字符)

s="hello shell"
echo ${s##*s} # 掐头 使用 ##
echo ${s%%s*}  # 去尾 使用 %%

字符串替换 (非贪婪模式)

s="hello shell"
echo ${s/shell/python} # 表示把shell替换成python 使用 /
输出:hello python 

字符串替换 (贪婪模式)

s="hello shell"
echo ${s//l/x} # 表示把所有l替换成x 使用 //
输出:hexxo shexx 

加上双引号不忽略前面都空格

s="hello shell"
echo "${s#hello}"
输出: shell  # 注意shell前面有个空格的

字符串比较

  • [ string1 = string2 ] 如果两个字符串相同,则结果为真
s1="abc"
s2="bcd"
[ "$s1" = "$s2" ]; echo $?
输出:1   # 代表false
  • [ string1 != string2 ] 如果两个字符串不相同,则结果为真
  • [ -n string ] 如果字符串不是空, 则结果为真
  • [ -z string ] 如果字符串是空,则结果为真
  • [[ “xxx” == x* ]] 在表达式中表示0或者多个字符
s="hello"
 [[ "$s" == h* ]] ; echo $? # 表示$s 是否等于以h开头后面任意字符的字符串
输出:0
  • [[ “xxx” == x?? ]] 在表达式中表示单个字符
s="hello"
 [[ "$s" == m?? ]] ; echo $? # 表示$s 是否等于以m开头的单个字符串
输出:1
  • [[ ]]是[ ]的扩展语法,在老的sh里并不支持,推荐使用[ ]
  • 字符串比较最好都加上双引号
  • 注意 [ ] 两边都有空格
# 如果不加上双引号这里有个坑
a="";
b="abc";
[ $a = $b ]; echo $?
输出:-bash: [: =: unary operator expected

# 加上双引号后
[ "$a" = "$b" ]; echo $?
输出:1 # 表示不成立

说明:如果字符串为空时,不加上双引号会引发错误,所以建议在对字符串操作���都加上双引号

算数判断

  • [ 2 -eq 2 ] 相等
  • [ 2 -ne 2 ] 不等
  • [ 3 -gt 1 ] 大于
  • [ 2 -ge 2 ] 大于等于
  • [ 3 -lt 4 ] 小于
  • [ 2 -le 2 ] 小于等于
  • (())也可以表示算术比较,((10>=8)), ((100==100)) 推荐使用这种

逻辑运算

  • 逻辑与
# -a 表示逻辑与
[ 2 -ge 1 -a 3 -ge 4 ]; echo $?  # 表示2大于1 并且 3大于4
输出:1 # 代表条件不成立

扩展语法
# && 表示逻辑与
[[ 2 -ge 1 && 3 -ge 4 ]]; echo $?  # 表示2大于1 并且 3大于4
输出:1 # 代表条件不成立
  • 逻辑或
# -o 表示逻辑或
[ 2 -ge 1 -o 3 -ge 4 ]; echo $?  # 表示2大于1 或者 3大于4
输出:0 # 代表条件成立

扩展语法
# || 表示逻辑或
[[ 2 -ge 1 || 3 -ge 4 ]]; echo $?  # 表示2大于1 或者 3大于4
输出:0 # 代表条件成立
  • 逻辑非
# ! 表示逻辑非
[ ! 2 -ge 1 ]; echo $?  # 表示 取反
输出:1 # 代表条件不成立

内置判断

  • -e file 如果文件存在,则结果为真
  • -d file 如果文件是一个子目录,则结果为真
  • -f file 如果文件是一个普通文件,则结果为真
  • -r file 如果请文件是可读,则结果为真
  • -w file 如果请文件是可写,则结果为真
  • -x file 如果请文件是可执行,则结果为真
  • -s file 如果文件的长度不为0,则结果为真
# 判断是不是目录
[ -d 文件名 ]; echo $?

逻辑控制

if结构

  • if [ 条件 ] ; then …; fi
  • if [ 条件] ; then …; else …fi
  • if [ 条件 ]; then … ; elif … fi
  • 简单的逻辑可以使用 && || 去替代
  • 条件可以使用命令返回值代替
# 判断文件是否存在,如果文件存在打印exist,如果文件不存在打印 not exist
if [ -e test ]; then echo exist; else echo not exist; fi  

# 判断ls命令下是否有文件
if ls test; then echo exist; else not exist; fi # 如果ls命令下存在文件则输出exist否则输出not exist

# 简写 如果执行ls命令返回的是true,则输出exist,如果返回的是false 则输出not exist
ls test && echo exist || echo not exist  

#  简写并不完全等价于if逻辑判断,这里有个坑。例如
ls test || echo exist && echo not exist  # 这样的话会输出 not exist

# 原因,执行||后面的语句 时, 只有当||前面的语句返回的是false时才会执行,如果是true就不会执行了,直接执行 && 后面的语句了。

# 试试下面的例子
echo "1" && echo "2" || echo "3" && echo "4" || echo "5" || echo "6" && echo "7" && echo "8" || echo "9"

for循环

  • for (( i=0; i<10; i++)) ;
    do

    done

  • for x in ${array[@]};
    do … ;
    done

# 普通循环
for (( i=0; i<10; i++ ));
do
echo $i; # 打印 i
done

# 循环打印数组
array=(1 2 3 4 5)
for ((i=0; i<${#array[@]}; i++)); do echo ${array[i]}; done

# for 遍历循环
array=(1 2 3 4 5)
for x in ${array[@]}; do echo $x; done

# 循环取目录下的所有文件
for x in *; do echo $x; done # * 代表当前目录的所有文件 或者 用`ls`(但是用`ls`有个坑 如果文件名称中间有空格会被当成两个文件)

while循环

i = 0
while [ $i -lt 3 ];
do 
echo $i;
(( i++ ));
done
输出:
0 
1
2

read 读取用户输入的值,相当于python的input

# 让用户输入内容
read re
如输入:abc
echo $re
输出 abc

# 有提示信息的输入
read -p "请输入内容" re; echo $re # -p 后面可以加提示信息

# 使用whlie循环读取文件的内容
while read line; do echo $line; done < txet(文件名)

# 将输入的内容写入到文件中
while read line; do echo $line; done > txet(文件名)

��出循环

  • return 函数返回
  • exit 脚步退出
  • break 退出当前循环,默认为1
  • break 2 退出两层循环
  • continue 跳过当前的循环,进入下一次循环
  • continue 2 跳到上层循环的下次循环中

Shell运行环境概念

  • bash 是一个进程
    • bash下还可以再重新启动一个shell,这个shell是子shell, 原shell会复制自身给它
    • 在子shell中定义的变量,会随着子shell的消亡而消失,相当于python中函数中定义的变量,随着函数的执行完成函数中的变量也消失了
  • ( ) 在子shell中运行,外层是取不到( )中的变量的
# 子shell中运行
a=2
[02158664@izuf60jasqavbxb9efockpz ~]$ (a=1; echo $a); echo $a
输出:
1
2
可以看出在外层中echo $a 不会因为在(a=1)重新新赋值而受到影响。
  • { } 当前shell中运行
  • $?命令执行返回都结果,是true 还是 false
    • 任何命令执行都会有一个返回值
    • 0表示正确
    • 非0表示错误
true
echo $?
输出: 0

false 
echo $?
输出:1

ls
echo $?
输出: 0
  • $$ 当前bash(脚本)执行的pid

  • & 后台执行

for ((i=0; i<5; i++)); do echo $i; sleep 2; done &
# sleep 休眠2秒
# &在后台运行
  • $! 运行在后台的最后一个进程PID

  • jobs 查看后台运行的任务现在执行的状态和任务序号

  • bg num 当使用crlt + z 把前台任务切换到后台时,会暂停任务,使用 bg + 任务的序号 可以继续执行任务

  • fg num 把后台任务显示到前台,有这样一种场景,当你使用vim编辑文本时,想先退出去做其他的操作,这时可以使用crlt + z把vim切到后台,等其他操作完成时 使用 fg + 任务序号 在把vim切换到前台,继续操作。

vim常用快捷键

  • i进入编辑模式
  • v 剪切复制模式
  • esc 退出编辑模式

剪切复制模式下

  • y 复制
  • d 剪切
  • p 粘贴

esc 模式下

  • gg 跳到文件头
  • G 跳到文件尾
  • nG 跳到指定行 n代表行号
  • dd 删除整行

冒号模式下输入命令 在esc下按 shift + : 进入冒号命令行模式

  • q 退出不保存
  • q! 强制退出不保存
  • wq 保存退出
  • wq! 强制保存退出
  • set nu 显示行号
  • set nonu 不显示行号
  • /string 搜索指定字符串

Shell 输入输出

  • read 用来读取输入,并赋值给变量
  • echo,printf 可以简单输出变量
  • > file 将输出重定向到另一个文件,等价于tee
# 将内容写入到文件中,会覆盖原有的内容
echo "hello shell" > txt
  • >>表示追加等价于tee -a
# 将内容追加写入到文件中,不会覆盖原有的内容
echo "hello python" >> txt
  • < file 输入重定向
# 将原本是用键盘输入的重定向给了txt文件,变成了以文件的内容输入,
# 所以就是将文件的内容输出
read x < txt # read一次只读取一行
  • | 表示管道,也就是前一个命令的输出作为下一个命令的输入

文件描述符

# 将执行命令时提示错误的信息输入到文件中
# 如ls一个不存在到目录会提示:No such file or directiry
ls aaa > txt 2>&1
# 说明:当我们执行一个命令的时候会有一个true的状态和false的状态,
# 当命令执行成功返回true时会重定向到1中(1现在被我改成了txt文件)
# 当执行不成功返回false时会重定向2中(2默认是输出到命令窗口)
# >&1的作用就是告诉机器把错误的信息不要打印到命令窗口了,你跟我打印到txt文件中去