Shell与Bash
上图从某度上找的,可以大概说明些情况。我们都知道,linux主要是靠命令来进行生产控制的,而Shell
在整个生产控制过程中充当翻译的角色,将我们输入的高级语言转换成机器可以识别的二进制命令执行。shell是一个提供了命令行输入界面的程序,而通过shell程序执行shell脚本不仅可以将一条条单个命令通过逻辑控制(if、for等)高效重复地运行输出,还可以在其中调用其它程序(如convert、awk、grep等)。为了适应不同系统和机器,后期衍生出了众多的shell版本,而Bash
只是众多Shell中的其中一种。为了使脚本的可移植性或兼容性,通常应在文件开头声明,如:#!/bin/bash。而.sh文件是一种标准或规范,#!/bin/sh的声明显然太过宽泛,但一般符合POSIX
标准规范的基本可以兼容运行。
然而,我的 Ubuntu 下显示默认/bin/sh直接指向dash
,这是因为bash过于复杂和庞大,后期版本中逐渐被dash(从原bash中抽取出必要的,作为轻量化来使用)取代,但相对而言在脚本的写法上没有bash丰富。
@lenovo ➜ ~ file -h /bin/sh
/bin/sh: symbolic link to dash
变量
- 名称大小写随意
- =等号两边不能有空格
- 取值时在名称前加 $ (在命令行中亦可生效),赋值不用
- 运行命令前会先将变量替换为具体值
- 命令行模式下可以传变量值,第一个值传给脚本文件中的$1,第二个值传给脚本文件中的$2,以此类推
- 在脚本中可以写的命令同样适用于在命令行中
- '单引号' 意为里面都是作为字符串的,而 "双引号" 则可以在里面夹杂字符串和变量(注意带$)
- 可以将命令输出的东西直接赋值,例如:var=$(ls /etc | wc -l)
特殊变量
系统变量 | 描述 |
---|---|
$0 | Bash 脚本的名称 |
$1 - $9 | Bash 脚本的前9个参数 |
$# | 多少个参数传递给 Bash 脚本 |
$@ | 提供给 Bash 脚本的所有参数 |
$? | 最近运行的进程的退出状态 |
$$ | 当前脚本的进程 ID |
$USER | 运行脚本的用户的用户名 |
$HOSTNAME | 运行脚本的计算机的主机名 |
$SECONDS | 自脚本启动以来的秒数 |
$RANDOM | 每次引用时返回一个不同的随机数 |
$LINENO | 返回 Bash 脚本中的当前行 |
导出变量
script1.sh
#!/bin/zsh
var1=葡萄
var2=红枣
# 验证变量值
echo $0 :: var1 : $var1, var2 : $var2
# 将当前线程运行中的 var1 变量导入到第二个脚本中
export var1
bash ./script2.sh
# 验证导入其它变量之后的变量值
echo $0 :: var1 : $var1, var2 : $var2
script2.sh
#!/bin/zsh
# 验证变量值
echo $0 :: var1 : $var1, var2 : $var2
# 改变这些变量
var1=酸奶
var2=玉米
[Running] /bin/zsh "/home/livejq/文档/script1.sh"
/home/livejq/文档/script1.sh :: var1 : 葡萄, var2 : 红枣
./script2.sh :: var1 : 葡萄, var2 :
/home/livejq/文档/script1.sh :: var1 : 葡萄, var2 : 红枣
[Done] exited with code=0 in 0.02 seconds
运算
let 函数
# let 是 bash 的其中一个函数
# 当没有双引号时,运算过程中不允许有空格
let a=5+4
echo $a # 9
let "a = $a + 4"
echo $a # 13
let ++a
echo $a # 14
let "a = 4 * 5"
echo $a # 20
let b=5\*4
echo $b # 20
let "a = $1 + 30"
echo $a # 30 + 命令行 第一个参数
expr 函数
# 操作数和运算符之间必须要有空格
expr 5 + 4
expr "5 + 4"
expr 5+4
expr 5 \* $1
expr 11 % 2
a=$( expr 10 - 3 )
echo $a # 7
发现了没有,跟上面的 let 不同,expr 只是将运算后的结果输出,而要想跟 let 一样具有赋值功能,则可以搭配$( )。
输出
@lenovo ➜ 文档 bash ./Bash.sh 2
9
5 + 4
5+4
10
1
7
@lenovo ➜ 文档
操作 | 运算结果 |
---|---|
+,-,\ *,/ | 加,减,乘,除 |
var与var | 相当于var=var+1 |
var--与--var | 相当于var=var-1 |
% | 除后取余 |
- 符号在前
a=$( expr 10 - 3 )
echo $a # 7
let b=++a/8
echo $b # 1
echo $a # 8
- 符号在后
a=$( expr 10 - 3 )
echo $a # 7
let b=a++/8
echo $b # 0 只取整数部分
echo $a # 8
双括号
a=$(( 4 + 5 ))
echo $a # 9
a=$((3+5))
echo $a # 8
b=$(( a + 3 ))
echo $b # 11
b=$(( $a + 4 ))
echo $b # 12
(( b++ ))
echo $b # 13
(( b += 3 ))
echo $b # 16
a=$(( 4 * 5 ))
echo $a # 20
赋值时需要在双括号前加 $ ,双括号中可以不空格(但为了美观,还是有必要空一下的)。
变量长度
a='Hello World'
echo ${#a} # 11
b=4953
echo ${#b} # 4
if 语句
#!/bin/zsh
# 注意缩进,增强可读性
if [ $1 -gt 100 ]
then
echo "太大了!"
if (( $1 % 2 == 0 ))
then
echo "你给的是一个偶数"
fi
elif [ $2 == 'yes' ]
then
echo "你输入的是 yes"
else
echo "你没有输入 yes"
fi
pwd
不加双引号对于输出变量值比较方便,而输出字符串如果不加双引号的话需要注意转义字符。
[ $1 -gt 100 ] 这对中括号是对此命令的一个测试,在命令行上等价于 test 10 -gt 100,随即可以键入 echo $? 以查看结果:0 为 true,1 为 false 。双括号是对运算表达式的检查,可以作为变量赋值$(( ))**。fi 是 if 的倒写,代表 if 整个语句的结束
输出
@lenovo ➜ 文档 bash ./Bash.sh 120
太大了!
你给的是一个偶数
/home/livejq/文档
@lenovo ➜ 文档 bash ./Bash.sh 90 yes
你输入的是 yes
/home/livejq/文档
运算符
单目
操作符 | 为 true 则执行 then~fi 代码块 |
---|---|
! value | 取反 |
-n string | string 字符不为空 |
-z string | string 字符为空 |
-b FILE | 文件是一个块特殊文件 |
-c FILE | 文件是一个字符特殊文件 |
-e FILE | 文件存在,即使是目录也可以 |
-d FILE | 文件存在且是一个目录 |
-s FILE | 文件存在且不为空,即文件大小要大于0 |
-h FILE | 文件是一个软链接 |
-L FILE | 文件是一个符号链接(硬链接测试没生效) |
-f FILE | 文件是常规文件,而不是目录或者特殊文件 |
-r FILE | 文件存在且有读取权限 |
-w FILE | 文件存在且有写入取权限 |
-x FILE | 文件存在且有执行权限 |
双目
操作符 | 为 true 则执行 then~fi 代码块 |
---|---|
string = string | 字符串匹配 |
string != string | 字符串不匹配 |
integer -eq integer2 | 等于( equals ) |
integer -gt integer2 | 大于( greate then ) |
integer -lt integer2 | 小于( less then ) |
integer -ge integer2 | 大于或等于( greate or equals ) |
integer -le integer2 | 小于或等于( less or equals ) |
file1 -nt file2 | 第一个文件比第二个文件新 |
file1 -ot file2 | 第一个文件比第二个文件旧 |
file1 -ef file2 | 第一个文件和第二个文件一样(软链接、硬链接等) |
xx && xx | 且(两者都为 true 才为真),与 -a 类似 |
xx || xx | 或(只要其中一个为 true 即为真 ),与 -o 类似 |
且、或
#!/bin/zsh
# 注意缩进,增强可读性
a=5
b=200
if [[ $a -lt 10 && $b -gt 100 ]]
then
echo "It's true"
else
echo "It's false"
fi
if [ $a -lt 10 -a $b -gt 100 ]
then
echo "It's true"
else
echo "It's false"
fi
注意:[ ] 括号旁边需要至少空一格,内部操作符两边也要空格,否则会出现 bad pattern 的异常
输出
[Running] /bin/zsh "/home/livejq/文档/Bash.sh"
It's true
It's true
[Done] exited with code=0 in 0.014 seconds
case 语句
用于替代频繁 if/else ,简化语句。
#!/bin/zsh
# 注意缩进,增强可读性
case $1 in
start)
echo starting
;;
stop)
echo stoping
;;
restart)
echo restarting
;;
*)
echo don\'t know
;;
esac
)代表匹配结束,;; 代表一组模式的结束,esac 是 case 的倒写,代表 case 整个语句的结束,* 代表匹配任意情况(与 java 中的 default 类似)
输出
@lenovo ➜ 文档 bash ./Bash.sh start
starting
@lenovo ➜ 文档 bash ./Bash.sh restart
restarting
@lenovo ➜ 文档 bash ./Bash.sh all
don't know
@lenovo ➜ 文档
case 高级用法
# 搭配通配符使用
space_free=$( df -h | awk '{ print $5 }' | sort -n | tail -n 1 | sed 's/%//' )
case $space_free in
[1-5]*)
echo 磁盘空间充裕
;;
[6-7]*)
echo 磁盘空间在未来将会出现瓶颈
;;
8*)
echo 你可能需要清理掉一些垃圾来增加磁盘容量
;;
9*)
echo 磁盘容量不足,电脑正面临崩溃!
;;
*)
echo 没有匹配项
;;
esac
循环 while、until 和 for
counter=1
while [ $counter -le 10 ]
do
echo $counter
((counter++))
done
echo 输出完毕
until [ $counter -gt 10 ]
do
echo $counter
((counter++))
done
echo 输出完毕
for value in {1..10}
do
echo $value
done
echo 输出完毕
输出结果相同
[Running] /bin/zsh "/home/livejq/文档/Bash.sh"
1
2
3
4
5
6
7
8
9
10
输出完毕
[Done] exited with code=0 in 0.023 seconds
for 的其它额外用法
# 用默认的空格作为分隔符构造一个字符串列表
sports='篮球 足球 羽毛球'
for sport in $sports
do
echo $sport
done
echo 输出完毕
输出
[Running] /bin/zsh "/home/livejq/文档/Bash.sh"
篮球 足球 羽毛球
输出完毕
[Done] exited with code=0 in 0.023 seconds
for value in {10..0..2}
do
echo $value
done
echo 输出完毕
输出
[Running] /bin/zsh "/home/livejq/文档/Bash.sh"
10
8
6
4
2
0
输出完毕
[Done] exited with code=0 in 0.014 seconds
for 的高级用法
# 搭配通配符使用
for value in $1/*.html
do
cp $value $1/$( basename -s .html $value ).php
done
echo "已将某个目录下的所有 html 后缀的文件转换为 php 后缀(保留源文件)"
for 跟 java 一样有 break 和 continue,用法类似。
select 语句
menu='纯净水 啤酒 可乐 果汁 不需要'
# 更改系统变量 PS3 的值,以便将提示设置为更具描述性的内容(默认为#?)
PS3='先生,请问您需要喝点什么呢: '
select drink in $menu
do
if [ $drink = '不需要' ]
then
break
fi
echo 先生,您的 $drink
done
echo "随时为您服务!"
输出
@lenovo ➜ 文档 bash ./Bash.sh
1) 纯净水
2) 啤酒
3) 可乐
4) 果汁
5) 不需要
先生,请问您需要喝点什么呢: 2
先生,您的 啤酒
先生,请问您需要喝点什么呢: 5
随时为您服务!
@lenovo ➜ 文档
括号总结
类型 | 描述 |
---|---|
$( ) | 执行命令 |
$(( )) | 将算术运算后的结果赋值 |
(( )) | 仅仅执行算术运算 |
[ ] | 括号旁边需要空格,返回真假 |
[[ ]] | 用于添加且、或,同样返回真假 |
${ } | ${var}与$var一样可以的取值,只是${ }可以对变量作更加丰富的操作,如${#var}输出 var 长度 |
( ) | 返回数组 |
学完这些,写个基本的 Bash 脚本基本没问题了 😌
评论区