什么是 Bash
简介
Bash(GNU Bourne-Again Shell)是一个为 GNU 计划编写的 Unix shell,它是许多 Linux 平台默认使用的 shell。
shell 是一个命令解释器,是介于操作系统内核与用户之间的一个绝缘层。准确地说,它也是能力很强的计算机语言,被称为解释性语言或脚本语言。它可以通过将系统调用、公共程序、工具和编译过的二进制程序”粘合“在一起来建立应用,这是大多数脚本语言的共同特征,所以有时候脚本语言又叫做“胶水语言”。
事实上,所有的 UNIX 命令和工具再加上公共程序,对于 shell 脚本来说,都是可调用的。Shell 脚本对于管理系统任务和其它的重复工作的例程来说,表现的非常好,根本不需要那些华而不实的成熟紧凑的编译型程序语言。
运行 Bash 脚本的方式:
#!/bin/bash
#这一行是表示使用 /bin/bash 作为脚本的解释器
#这行要放在脚本的行首并且不要省略。
# 使用shell来执行
sh hello.sh
# 使用bash来执行
bash hello.sh
# 使用.来执行
. ./hello.sh
# 使用source来执行
source hello.sh
# 还可以赋予脚本所有者执行权限,允许该用户执行该脚本
chmod u+rx hello.sh
./hello.sh
bash特殊字符(上)
注释(#)
行首以 # 开头(除#!之外)的是注释。#! 是用于指定当前脚本的解释器,我们这里为 bash,且应该指明完整路径,所以为 /bin/bash
分号(;)
使用分号 ; 可以在同一行上写两个或两个以上的命令。
点号(.)
等价于 source 命令
bash 中的 source 命令用于在当前 bash 环境下读取并执行 FileName.sh 中的命令。
source test.sh
. test.sh
引号
双引号(")
“STRING” 将会阻止(解释)STRING 中大部分特殊的字符。后面的实验会详细说明。
单引号(’)
‘STRING’ 将会阻止 STRING 中所有特殊字符的解释,这是一种比使用"更强烈的形式。后面的实验会详细说明。
反引号()</p> <p>命令替换</p> <p>反引号中的命令会优先执行,如:</p> <p>cp
mkdir backtest.sh back</p> <p>ls</p> <p>先创建了 back 目录,然后复制 test.sh 到 back 目录。</p> <p>冒号(:)</p> <p>空命令</p> <p>等价于“NOP”(no op,一个什么也不干的命令)。也可以被认为与 shell 的内建命令 true 作用相同。“:”命令是一个 bash 的内建命令,它的退出码(exit status)是(0)。</p> <p>#!/bin/bash</p> <p>while :</p> <p>do</p> <p> echo "endless loop"</p> <p>done</p> <p>等价于</p> <p>#!/bin/bash</p> <p>while true</p> <p>do</p> <p> echo "endless loop"</p> <p>done</p> <p>可以在 if/then 中作占位符:</p> <p>#!/bin/bash</p> <p>condition=5</p> <p>if 【 $condition -gt 0 】 </p> <p>#gt表示greater than,也就是大于,同样有-lt(小于),-eq(等于)</p> <p>then : # 什么都不做,退出分支</p> <p>else</p> <p> echo "$condition"</p> <p>fi</p> <p>变量扩展/子串替换</p> <p>在与 > 重定向操作符结合使用时,将会把一个文件清空,但是并不会修改这个文件的权限。如果之前这个文件并不存在,那么就创建这个文件。</p> <p>: > test.sh # 文件“test.sh”现在被清空了</p> <p># 与 cat /dev/null > test.sh 的作用相同</p> <p># 然而,这并不会产生一个新的进程, 因为“:”是一个内建命令</p> <p>在与 ] 重定向操作符结合使用时,将不会对预先存在的目标文件 : ] target_file 产生任何影响。如果这个文件之前并不存在,那么就创建它。</p> <p>问号(?)</p> <p>测试操作符</p> <p>在一个双括号结构中,? 就是 C 语言的三元操作符,如:</p> <p>vim test.sh</p> <p>输入如下代码,并保存:</p> <p> #!/bin/bash</p> <p> a=10</p> <p> (( t=a<50?8:9 ))</p> <p> echo $t</p> <p>运行测试</p> <p>bash test.sh</p> <p>8</p> <p>美元符号($)</p> <p>引用变量</p> <p>bash特殊字符(下)</p> <p>小括号(( ))</p> <p>在括号中的变量,由于是在子 shell 中,所以对于脚本剩下的部分是不可用的。父进程,也就是脚本本身,将不能够读取在子进程中创建的变量,也就是在子 shell 中创建的变量。如:</p> <p>vim test20.sh</p> <p>输入代码:</p> <p>#!/bin/bash</p> <p>a=123</p> <p>( a=321; )</p> <p>echo "$a" #a的值为123而不是321,因为括号将判断为局部变量</p> <p>运行代码:</p> <p>bash test20.sh</p> <p>a = 123</p> <p>在圆括号中 a 变量,更像是一个局部变量。</p> <p>2.初始化数组</p> <p>创建数组</p> <p>vim test21.sh</p> <p>输入代码:</p> <p>#!/bin/bash</p> <p>arr=(1 4 5 7 9 21)</p> <p>echo ${arr【3】} # get a value of arr</p> <p>运行代码:</p> <p>bash test21.sh</p> <p>7</p> <p>大括号({ })</p> <p>文件名扩展</p> <p>复制 t.txt 的内容到 t.back 中</p> <p>vim //代码效果参考:http://hnjlyzjd.com/hw/wz_24539.html<p></p> test22.sh</p> <p>输入代码:</p> <p>#!/bin/bash</p> <p>if 【 ! -w 't.txt' 】;</p> <p>then</p> <p> touch t.txt</p> <p>fi</p> <p>echo 'test text' ] t.txt</p> <p>cp t.{txt,back}</p> <p>运行代码:</p> <p>bash test22.sh</p> <p>查看运行结果:</p> <p>ls</p> <p>cat t.txt</p> <p>cat t.back</p> <p>注意: 在大括号中,不允许有空白,除非这个空白被引用或转义。</p> <p>代码块</p> <p>代码块,又被称为内部组,这个结构事实上创建了一个匿名函数(一个没有名字的函数)。然而,与“标准”函数不同的是,在其中声明的变量,对于脚本其他部分的代码来说还是可见的。</p> <p>vim test23.sh</p> <p>输入代码:</p> <p>#!/bin/bash</p> <p>a=123</p> <p>{ a=321; }</p> <p>echo "a = $a"</p> <p>运行代码:</p> <p>bash test23.sh</p> <p>a = 321</p> <p>变量 a 的值被更改了。</p> <p>中括号(【 】)</p> <p>条件测试</p> <p>条件测试表达式放在 【 】 中。下列练习中的 -lt (less than)表示小于号。</p> <p>vim test24.sh</p> <p>输入代码:</p> <p>#!/bin/bash</p> <p>a=5</p> <p>if 【 $a -lt 10 】</p> <p>then</p> <p> echo "a: $a"</p> <p>else</p> <p> echo 'a>=10'</p> <p>fi</p> <p>运行代码:</p> <p>bash test24.sh</p> <p>a: 5</p> <p>数组元素</p> <p>在一个 array //代码效果参考:http://hnjlyzjd.com/hw/wz_24547.html<p></p> 结构的上下文中,中括号用来引用数组中每个元素的编号。</p> <p>vim test25.sh</p> <p>输入代码:</p> <p>#!/bin/bash</p> <p>arr=(12 22 32)</p> <p>arr【0】=10</p> <p>echo ${arr【0】}</p> <p>运行代码:</p> <p>bash test25.sh</p> <p>10</p> <p>尖括号()</p> <p>重定向</p> <p>test.sh > filename:重定向 test.sh 的输出到文件 filename 中。如果 filename 存在的话,那么将会被覆盖。</p> <p>test.sh &> filename:重定向 test.sh 的 stdout(标准输出)和 stderr(标准错误)到 filename 中。</p> <p>test.sh >&2:重定向 test.sh 的 stdout 到 stderr 中。</p> <p>test.sh ] filename:把 test.sh 的输出追加到文件 filename 中。如果 filename 不存在的话,将会被创建。</p> <p>竖线(|)</p> <p>管道</p> <p>分析前边命令的输出,并将输出作为后边命令的输入。这是一种产生命令链的好方法。</p> <p>破折号(-)</p> <p>波浪号(~)</p> <p>目录</p> <p>表示 home 目录。</p> <p>特殊变量</p> <p>位置参数</p> <p>从命令行传递到脚本的参数:$0,$1,$2,$3…</p> <p>$0 就是脚本文件自身的名字,$1 是第一个参数,$2 是第二个参数,$3 是第三个参数,然后是第四个。9 之 后 的 位 置 参 数 就 必 须 用 大 括 号 括 起 来 了 , 比 如 , 9 之后的位置参数就必须用大括号括起来了,比如,9之后的位置参数就必须用大括号括起来了,比如,{10},11 , {11},11,{12}。</p> <p>$# : 传递到脚本的参数个数</p> <p>$* : 以一个单字符串显示所有向脚本传递的参数。与位置变量不同,此选项参数可超过 9 个</p> <p>$$ : 脚本运行的当前进程 ID 号</p> <p>$! : 后台运行的最后一个进程的进程 ID 号</p> <p>$@ : 与 $* 相同,但是使用时加引号,并在引号中返回每个参数</p> <p>$: 显示 shell 使用的当前选项,与 set 命令功能相同</p> <p>$? : 显示最后命令的退出状态。 0 表示没有错误,其他任何值表明有错误。</p> <p>位置参数实例</p> <p>vim test30.sh</p> <p>#!/bin/bash</p> <p># 作为用例, 调用这个脚本至少需要10个参数, 比如:</p> <p># bash test.sh 1 2 3 4 5 6 7 8 9 10</p> <p>MINPARAMS=10</p> <p>echo</p> <p>echo "The name of this script is \"$0\"."</p> <p>echo "The name of this script is \"
basename $0\"."</p> <p>echo</p> <p>if 【 -n "$1" 】 # 测试变量被引用.</p> <p>then</p> <p>echo "Parameter #1 is $1" # 需要引用才能够转义"#"</p> <p>fi</p> <p>if 【 -n "$2" 】</p> <p>then</p> <p>echo "Parameter #2 is $2"</p> <p>fi</p> <p>if 【 -n "${10}" 】 # 大于$9的参数必须用{}括起来.</p> <p>then</p> <p>echo "Parameter #10 is ${10}"</p> <p>fi</p> <p>echo "-----------------------------------"</p> <p>echo "All the command-line parameters are: "$*""</p> <p>if 【 $# -lt "$MINPARAMS" 】</p> <p>then</p> <p> echo</p> <p> echo "This script needs at least $MINPARAMS command-line arguments!"</p> <p>fi</p> <p>echo</p> <p>exit 0</p> <p>运行代码:</p> <p>bash test30.sh 1 2 10</p> <p>The name of this script is "test.sh".</p> <p>The name of this script is "test.sh".</p> <p>Parameter #1 is 1</p> <p>Parameter #2 is 2</p> <p>-----------------------------------</p> <p>All the command-line parameters are: 1 2 10</p> <p>This script needs at least 10 command-line arguments!</p> <p>基本运算符</p> <p>算数运算符</p> <p>vim test.sh</p> <p>#!/bin/bash</p> <p>a=10</p> <p>b=20</p> <p>val=
expr $a + $b</p> <p>echo "a + b : $val"</p> <p>val=
expr $a - $b</p> <p>echo "a - b : $val"</p> <p>val=
expr $a * $b</p> <p>echo "a * b : $val"</p> <p>val=
expr $b / $a</p> <p>echo "b / a : $val"</p> <p>val=
expr $b % $a`
echo "b % a : $val"
if 【 $a == $b 】
then
echo "a == b"
fi
if 【 $a != $b 】
then
echo "a != b"
fi
运行
bash test.sh
a + b : 30
a - b : -10
a b : 200
b / a : 2
b % a : 0
a != b
原生 bash 不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
注意使用的反引号(esc 键下边)
表达式和运算符之间要有空格 $a + $b 写成 a + a+a+b 不行
条件表达式要放在方括号之间,并且要有空格 【 $a == b 】 写 成 【 b 】 写成 【b】写成【a==$b】 不行
乘号()前边必须加反斜杠()才能实现乘法运算
关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
#!/bin/bash
a=10
b=20
if 【 $a -eq $b 】
then
echo "$a -eq $b : a == b"
else
echo "$a -eq $b: a != b"
fi
逻辑运算符
#!/bin/bash
a=10
b=20
if 【【 $a -lt 100 && $b -gt 100 】】
then
echo "return true"
else
echo "return false"
fi
if 【【 $a -lt 100 || $b -gt 100 】】
then
echo "return true"
else
echo "return false"
fi
字符串运算符
#!/bin/bash
a="abc"
b="efg"
if 【 $a = $b 】
then
echo "$a = $b : a == b"
else
echo "$a = $b: a != b"
fi
if 【 -n $a 】
then
echo "-n $a : The string length is not 0"
else
echo "-n $a : The string length is 0"
fi
if 【 $a 】
then
echo "$a : The string is not empty"
else
echo "$a : The string is empty"
fi
结果
abc = efg: a != b
-n abc : The string length is not 0
abc : The string is not empty
文件测试运算符
实例:
#!/bin/bash
file="/home/shiyanlou/test.sh"
if 【 -r $file 】
then
echo "The file is readable"
else
echo "The file is not readable"
fi
if 【 -e $file 】
then
echo "File exists"
else
echo "File not exists"
fi
结果
The file is readable
File exists
思考
浮点运算,比如实现求圆的面积和周长。
expr 只能用于整数计算,可以使用 bc 或者 awk 进行浮点数运算。
#!/bin/bash
radius=2.4
pi=3.14159
girth=$(echo "scale=4; 3.14 2 $radius" | bc)
area=$(echo "scale=4; 3.14 $radius $radius" | bc)
echo "girth=$girth"
echo "area=$area"
以上代码如果想在环境中运行,需要先安装 bc。
sudo apt-get update
sudo apt-get install bc
流程控制
if else
if
if 语句语法格式:
if condition
then
command1
command2
...
commandN
fi
if else
if else 语法格式:
if condition
then
command1
command2
...
commandN
else
command
fi
if-elif-else 语法格式:
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
for 循环
for 循环一般格式为:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
while 语句
while 循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:
while condition
do
command
done
无限循环
无限循环语法格式:
while :
do
command
done
或者
while true
do
command
done
或者
for (( ; ; ))
until 循环
until 循环执行一系列命令直至条件为真时停止。 until 循环与 while 循环在处理方式上刚好相反。 一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。 until 语法格式:
until condition
do
command
done
case
Shell case 语句为多选择语句。可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。case 语句格式如下:
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 捕获该值,再执行后面的命令。
下面的脚本提示输入 1 到 4,与每一种模式进行匹配:
echo 'Enter a number between 1 and 4:'
echo 'The number you entered is:'
read aNum
case $aNum in
1) echo 'You have chosen 1'
;;
2) echo 'You have chosen 2'
;;
3) echo 'You have chosen 3'
;;
4) echo 'You have chosen 4'
;;
) echo 'You did not enter a number between 1 and 4'
;;
esac
输入不同的内容,会有不同的结果,例如:
Enter a number between 1 and 4:
The number you entered is:
3
You have chosen 3
跳出循环
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break 和 continue。
break 命令
break 命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于 5。要跳出这个循环,返回到 shell 提示符下,需要使用 break 命令。
#!/bin/bash
while :
do
echo -n "Enter a number between 1 and 5:"
read aNum
case $aNum in
1|2|3|4|5) echo "The number you entered is $aNum!"
;;
) echo "The number you entered is not between 1 and 5! game over!"
break
;;
esac
done
执行以上代码,输出结果为:
Enter a number between 1 and 5:3
The number you entered is 3!
Enter a number between 1 and 5:7
The number you entered is not between 1 and 5! game over!
continue
continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。 对上面的例子进行修改:
#!/bin/bash
while :
do
echo -n "Enter a number between 1 and 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "The number you entered is $aNum!"
;;
) echo "The number you entered is not between 1 and 5!"
continue
echo "game over"
;;
esac
done
运行代码发现,当输入大于 5 的数字时,该例中的循环不会结束,语句 echo “Game is over!” 永远不会被执行。
EOF
本文作者: liftsail 本文链接: 关于博主: 评论和私信会在第一时间回复。或者直接私信我。 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处! 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。