操作系统简史
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 通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录
unset
取消变量
# 定义变量
a="hello"
echo $a
输出:hello
# 取消变量
unset a
echo $a
输出: #输出是空的,因为变量a不存在了
数组变量
# 定义数组
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"
[ "$a" = "$b"]; echo $?
输出: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; then ... 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
退出循环
-
return
函数返回 -
exit
脚步退出 -
break
退出当前循环,默认为1 -
break 2
退出两层循环 -
continue
跳过当前的循环,进入下一次循环 -
continue 2
跳到上层循环的下次循环中
Shell运行环境概念
- bash 是一个进程
- bash下还可以再重新启动一个shell,这个shell是子shell, 原shell会复制自身给它
- 在子shell中定义的变量,会随着子shell的消亡而消失,相当于python中函数中定义的变量,随着函数的执行完成函数中的变量也消失了
- 父程序的自定义变量是无法在子程序内使用的。但是通过 export 将变量变成环境变量后,就能够在子程序下面应用了
-
( )
在子shell中运行,外层是取不到( )中的变量的
# 子shell中运行
a=2
>(a=1; echo $a); echo $a
输出:
1
2
可以看出在外层中echo $a 不会因为在(a=1)重新新赋值而受到影响。
# 设置变量并可以在子程序中使用
>name=jack
>bash <==进入到所谓的子程序
>echo $name <==子程序:再次的 echo 一下;
<==嘿嘿!并没有刚刚设置的内容喔!
>exit <==子程序:离开这个子程序
>export name
>bash <==进入到所谓的子程序
>echo $name <==子程序:在此执行!
jack <==看吧!出现设置值了!
>exit <==子程序:离开这个子程序
-
{ }
当前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 + 任务的序号 可以继续执行任务
image.png
-
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
用来读取输入,并赋值给变量,相当于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(文件名)
-
echo,printf
可以简单输出变量 -
> file
将输出重定向到另一个文件,等价于tee
# 将内容写入到文件中,会覆盖原有的内容
echo "hello shell" > txt
-
>>
表示追加等价于tee -a
# 将内容追加写入到文件中,不会覆盖原有的内容
echo "hello python" >> txt
-
<
file 输入重定向
# 将原本是用键盘输入的重定向给了txt文件,变成了以文件的内容输入,
# 所以就是将文件的内容输出
read x < txt # read一次只读取一行
-
|
表示管道,也就是前一个命令的输出作为下一个命令的输入 -
tee
会同时将数据流分送到文件去与屏幕
tee [-a] file
选项与参数:
-a :以累加 (append) 的方式,将数据加入 file 当中!
# 实例
echo "abcd" | tee a.txt
abcd
cat a.txt
abcd
文件描述符
# 将执行命令时提示错误的信息输入到文件中
# 如ls一个不存在到目录会提示:No such file or directiry
ls aaa > txt 2>&1
# 说明:当我们执行一个命令的时候会有一个true的状态和false的状态,
# 当命令执行成功返回true时会重定向到1中(1现在被我改成了txt文件)
# 当执行不成功返回false时会重定向2中(2默认是输出到命令窗口)
# >&1的作用就是告诉机器把错误的信息不要打印到命令窗口了,你跟我打印到txt文件中去
输入/输出
printf格式输出
# 格式
printf '打印格式' 实际内容
%ns 那个 n 是数字, s 代表 string ,亦即多少个字符;
%ni 那个 n 是数字, i 代表 integer ,亦即多少整数;
%N.nf 那个 n 与 N 都是数字, f 代表 floating (浮点),
假设我共要十个位数,但小数点有两位,即为 %10.2f 啰!
# 实例
printf '%s %i %.2f\n' $(echo "abc" 123 10.89)
abc 123 10.89
函数
- $0 表示执行的程序,是相对于执行目的的路径
- $1,$2,$3 分别表示第几个参数,shell默认只能支持9个参数,使用shift可以传递更多的参数。
- $@,$* 表示所有的参数,不包含$0。
- ${#*}和${#@}表示位置参数的个数。
- 通过${*:1:3},${*:$#}来表示多个参数。
实例
#!/bin/bash
func(){
echo '$1='$1
echo '$2='$2
echo '$@='$@
echo '$*='$*
echo '$0='$0
echo '${#*}'=${#*}
echo '${#@}'=${#@}
echo '"$*"'="$*"
echo '"$@"'="$@"
echo '${*:1:3}='${*:1:3}
echo '${*:$#}='${*:$#}
echo '${*:1:1}='${*:1:1}
} ;
func 1 2 3 4 5 6 7 8 9
输出:
$1=1
$2=2
$@=1 2 3 4 5 6 7 8 9
$*=1 2 3 4 5 6 7 8 9
$0=func.sh
${#*}=9
${#@}=9
"$*"=1 2 3 4 5 6 7 8 9
"$@"=1 2 3 4 5 6 7 8 9
${*:1:3}=1 2 3
${*:$#}=9
${*:1:1}=1
$@ $* "$@" "$*"的区别
#!/bin/bash
count=0
f(){
echo '$@'=$@
for i in $@; do echo ${i}; ((count++)); done;
echo 'count='${count}
}
f 1 2 '3 "4 5" 6' 7 "8 9"
输出:
$@=1 2 3 "4 5" 6 7 8 9
1
2
3
"4
5"
6
7
8
9
count=9
==========================================
# "$@" 带双引号
count=0
f(){
echo '"$@"'="$@"
for i in "$@";do echo ${i}; ((count++)); done;
echo 'count='${count}
}
f 1 2 '3 "4 5" 6' 7 "8 9"
输出:
"$@"=1 2 3 "4 5" 6 7 8 9
1
2
3 "4 5" 6
7
8 9
count=5
==========================================
# $* 不带双引号
count=0
f(){
echo '$*'=$*
for i in $*;do echo ${i}; ((count++));done;
echo 'count='${count}
}
f 1 2 '3 "4 5" 6' 7 "8 9"
输出:
$*=1 2 3 "4 5" 6 7 8 9
1
2
3
"4
5"
6
7
8
9
count=9
==========================================
# "$*"带双引号
count=0
f(){
echo '"$*"'="$*"
for i in "$*"; do echo ${i}; ((count++)); done;
echo 'count='${count}
}
f 1 2 '3 "4 5" 6' 7 "8 9"
输出:
"$*"=1 2 3 "4 5" 6 7 8 9
1 2 3 "4 5" 6 7 8 9
count=1
记得使用"$@",不要直接使用$@
使用外部文件执行函数时,通过外部函数传参
- 创建一个test.sh文件
vim test.sh
- 在文件中写入如下函数:
#!/bin/bash
f()
{
name=$1
echo "${name}"
}
f $1
- 执行文件
$ bash test.sh abc(参数)
abc
执行方式
- chmod u+x test.sh; ./test.sh 在当前shell中执行
- bash test.sh 开启一个子shell执行
- source test.sh 在当前shell中执行,同./test.sh
DBUG代码
- bash -x 读取代码中的每一句,并执行,可以方便的看到每次的执行过程。
#!/bin/bash
# bash_dbug函数
$ cat bash_dbug.sh
f(){
for i in $(seq 1 5); do echo ${i}; done
}
f
$ bash -x bash_dbug.sh # dbug模式执行函数
+ f # 先执行f函数
++ seq 1 5 # 执行seq
+ for i in $(seq 1 5) # 循环
+ echo 1 # 输出
1
+ for i in $(seq 1 5) # 第二次循环
+ echo 2
2
+ for i in $(seq 1 5) # 第三次循环...
+ echo 3
3
+ for i in $(seq 1 5)
+ echo 4
4
+ for i in $(seq 1 5)
+ echo 5
5
- set -x 开启dbug模式,可以放在脚本的开头使用
#! /bin/bash
# set_dbug的函数
$ cat set_dbug.sh
set -x # 开启dbug
f(){
for i in $(seq 1 5); do echo ${i}; done
}
f
$ ./set_dbug.sh # 执行函数
+ f
++ seq 1 5
+ for i in $(seq 1 5)
+ echo 1
1
+ for i in $(seq 1 5)
+ echo 2
2
+ for i in $(seq 1 5)
+ echo 3
3
+ for i in $(seq 1 5)
+ echo 4
4
+ for i in $(seq 1 5)
+ echo 5
5
- set +x 关闭dbug模式
- set 可以进行局部调试,在需要调试的代码之前加上“set -x”,需要调试的代码之后加上“set +x”即可
网友评论