第一个Shell脚本
#! /bin/sh
echo "hello shell!"
依国际惯例这里以在终端里打印一句hello shell!
开始。
第一行的#!
是一个约定标记它告诉脚本这段脚本需要什么解释器来执行;
第二行的echo命令则负责向屏幕上输出一句话。
如何运行
运行shell程序有3种方法:
-
- chmod +x使文件具有可执行权限, 直接运行
-
- 直接调用解释器, 将脚本文件作为参数传入 (比如bash hi.sh)
-
- 使用source(也可用 . 代替)执行文件
通常情况下, 最方便的方式就是方式1, 通过方式1执行你需要在脚本第一行写好这段脚本由哪个解释器来解释,而通过方式2来执行则没有这个限制,写了也没用。
除此之外方式1与方式2执行命令就没有区别了, 但方式3执行的方式与前两种都不同:
使用source执行shell脚本时, 不会创建子进程, 而是在父进程中直接执行!
变量
和其它语言一样Shell中也有变量, 而且更简单, 但有一些比较特殊的地方.
-
- Shell中的变量
只有字符串
这一种类型
- Shell中的变量
-
- Shell中变量名与变量值没有长度限制
-
- Shell的变量也允许比较操作和整数操作, 只要变量中的字符串为数字
定义变量
variable_name=ghui
需要注意: = 两边不能加空格
。
当赋值语句包含空格时请加引号(单引号/双引号均可)
比如:
variable_name="ghui's blog"
Shell中的变量可以分为两种类型:
-
- 局部变量 (定义变量时在前面加local修饰符)
-
- 全局变量 (定义变量时不加任何修饰符)
与其它语言一样,局部变量的可见范围是代码块或函数内,全局变量在全局范围内可见。
看个简单的例子:
#! /bin/sh
num=111 #全局变量
func1()
{
local num=222 #局部变量
echo $num
}
echo "before---$num"
func1
echo "after---$num"
输出:
before---111
222
after---111
使用变量
使用一个定义过的变量, 只要在变量名前面加$
即可, 如:
name=ghui
echo $name
echo ${name} #{} 为了帮助解释器识别变量边界, 非必须
在使用变量时还有一个地方需要注意, 请看下面的例子:
#! /bin/sh
str='abc'
echo "1 print $str"
echo '2 print $str'
输出:
1 print abc
2 print $str
即: 被双引号括起来的变量会发生变量替换, 单引号不会
注释
Shell中注释使用#, 而且它不支持多行注释.
常用的字符串操作
字符串拼接
name="shell"
sayHi="hello, "$name" !"
sayHi2="hello, ${name} !"
echo $sayHi $sayHi2
注意: 上面说的单双引号引起的变量替换问题
获得字符串长度
string="abcd"
echo ${#string} #输出:4
截取字符串
str="hello shell"
echo ${str:2} #输出: llo shell
echo ${string:1:3} #输出:ell
if/else流程控制
基本语法结构:
if condition
then
do something
elif condition
then
do something
elif condition
then
do something
else
do something
fi
其中, elif语句和else语句非必须的.看个例子:
#! /bin/sh
a=1
if [ $1=$a ]
then
echo "you input 1"
elif [ $1=2 ]
then
echo "you input 2"
else
#do nothing
echo " you input $1"
fi
很简单, 不过这里有两个地方需要注意:
如果某个条件下的执行体为空, 则你就不能写这个条件 即下面这样会报错:
if condition
then
#do nothing
elif condition
then
# do nothing
#or
else
#do nothing
另外, [ ] 两边一定要加空格
, 下面这样都会报错:
if [$a=$b]
#or
if [ $a=$b]
#or
if [$a=$b ]
只有这样if [ $a=$b ]才是对的. 注意: 实际上这里的[]是test命令的一种形式, [是系统的一个内置命令,存在路径是/bin/[,它是调用test命令的标识, 右中括号是关闭条件判断的标识
。
因此下面的两个测试语句是等效的:
if test "2>3"
then
...
fi
和
if [ "2>3" ]
then
...
fi
除[]之外, shell语言中还有几种其它括号, 比如: 单小括号/双小括号/双中括号/… , 不同的括号有不同的用法
。
更多关于shell中, 括号的用法可以看看这个
switch流程控制
当条件较多时, 可以选择使用switch语句, shell中的switch语句的写法和其它语言还是有些不同的, 基本结构如下:
case expression in
pattern1)
do something... ;;
pattern2)
do something... ;;
pattern2)
do something... ;;
...
esac
看个例子:
#! /bin/sh
input=$1
case $input in
1 | 0)
str="一or零";;
2)
str="二";;
3)
str="三";;
*)
str=$input;;
esac
echo "---$str"
这个例子会根据你执行此脚本时传入的参数不同在屏幕上输出不同的值, 其中第一个case 1 | 0代表逻辑或. NOTE:
-
- ;;相当于其它语言中的break
-
- 每个pattern之后记得加)
-
- 最后记得加esac (即反的case)
for循环
Shell 脚本里最简单的循环当属 for 循环,有编程基础的朋友应该都有使用过 for 循环。 基本结构:
for name [in list]
do
...
done
其中,[]括起来的 in list, 为可选部分, 如果省略in list则默认为in "$@", 即你执行此命令时传入的参数列表.
看个例子,遍历当前目录下的所有txt文件, 并依次打开:
for file in *.txt
do
open $file
done
最简单的 for 循环如下所示,你只需将变量值依次写在 in 后面即可:
#!/bin/bash
for num in 1 2 3 4
do
echo $num
done
如果要循环的内容是字母表里的连续字母或连续数字,那么就可以按以下语法来写脚本:
#!/bin/bash
for x in {a..z}
do
echo $x
done
while循环
对于其它语言,如果你见过 for 循环却没见过 while 循环,那么你一定是学了个假语言。
在 while 循环里,每进行一次循环,条件都会被判断一次,来确定本次循环是否该继续。其实在循环次数比较少的情况下,for 循环与 while 循环效果差不多,但如果循环次数比较多,比如 10 万次,那么 while 循环的优势就体现出来了。
基本结构:
while condition
do
do something...
done
#!/bin/bash
n=1
while [ $n -le 4 ]
do
echo $n
((n++))
done
看个例子:
#! /bin/sh
i=0
while ((i<5));
do
((i++))
echo "i=$i"
done
输出:
i=1
i=2
i=3
i=4
i=5
NOTE: 需要去了解一下(())的用法
until循环
基本结构
until condition
do
do something...
done
看个例子:
#! /bin/sh
i=5
until ((i==0))
do
((i--))
echo "i=$i"
done
输出:
i=4
i=3
i=2
i=1
i=0
##循环套循环
像其它高级语言一样,循环是可以互相嵌套的。
比如下面这个例子,我们在 while 循环里再套入一个 for 循环:
#!/bin/bash
n=1
while [ $n -lt 6 ]
do
for l in {a..d}
do
echo $n$l
done
((n++))
done
这个脚本执行的结果应该是 1a, 1b, 1c, 1d, 2a, 2b … 5d。
##循环的内容是变化的
我们上面提到的 for 循环,循环变量要赋的值都列在了 in 后面的列表里了。
但这样灵活性太差,因为在很多情况下,循环变量要获得的值是不固定的。
就比如,有个变量要获得当前系统上所有用户,但因为每台电脑用户都不一样,我们根本就没办法将这个变量写死。
在这种情况下,我们可以使用 ls 命令将 /home 目录下所有用户都列出来,然后用循环变量依次获取它们。
完整代码如下:
#!/bin/bash
for user in `ls /home`
do
echo $user
done
当然,除了 ls ,Shell 还支持其它命令。
比如我们可以使用 date 命令获取当前系统时间,再依次打印出来:
$ for word in `date`
> do
> echo $word
> done
Thu
Apr
9
08:12:09
CST
2020
##变量值检查
我们在使用 while 循环时,经常需要判断一个变量的值是否大于或者小于某个数。
有时候这个数也是用另一个变量来表示,那么我们就需要判断这个变量的值是否是数字。
有三种判断方法:
#!/bin/bash
echo -n "How many times should I say hello? "
read ans
if [ "$ans" -eq "$ans" ]; then
echo ok1
fi
if [[ $ans = *[[:digit:]]* ]]; then
echo ok2
fi
if [[ "$ans" =~ ^[0-9]+$ ]]; then
echo ok3
fi
第一种方法看起来似乎是个废话,但实际上,-eq 只能用于数值间判断,如果是字符串则判断不通过,所以这就保证了 ans 是个数值型变量。
第二种方法是直接使用 Shell 的通配符对变量进行判断。
第三种方法就更直接了,使用正则表达式对变量进行判断。
我们直接来看一个例子:
#!/bin/bash
echo -n "How many times should I say hello? "
read ans
if [ "$ans" -eq "$ans" ]; then
n=1
while [ $n -le $ans ]
do
echo hello
((n++))
done
fi
在这个脚本里,我将要循环的次数传入到 ans 变量,然后脚本就具体打印几次 hello 。
为了保证我们传入的内容是数字,我们使用了 if [ "$ans" -eq "$ans" ]
语句来判断。如果我们传入的不是数字,则不会进入 while 循环。
##循环输出文本文件内容
如果你想按行依次循环输出文本文件的内容,可以这样操作:
#!/bin/bash
echo -n "File> "
read file
n=0
while read line; do
((n++))
echo "$n: $line"
done < $file
在这里,我们使用 read 命令将文本文件的内容读取存入 file 变量,然后再使用重定向(上述脚本最后一行)将 file 内容依次传入 while 循环处理再打印出来。
##死循环
有时候我们需要一直永远循环做某件事,那么我们就可以使用死循环。
达到这个目的很简单,只需使用 while true 即可。
#!/bin/bash
while true
do
echo -n "Still running at "
date
sleep 1
done
在以上这个脚本里,将每隔 1 秒打印一次 Still running at
具体时间 ,直到你按 Ctrl + C
终止这个脚本。
跳出循环
shell中也支持break跳出循环, continue跳出本次循环.用法与C, Java中相同
函数
要定义一个函数, 可以使用下面两种形式:
function funcname()
{
do something
}
或者
funcname ()
{
do something
}
看个例子
#! /bin/sh
# ad.sh 计算sum
add()
{
let "sum=$1+$2"
return $sum
}
add $1 $2
echo "sum=$?"
输入
ad 1 2
输出
sum=3
其中, $?在shell中保存的是上一条命令的返回值
NOTE:
- 函数必须先定义后使用
- 如果在函数中使用exit会退出脚本, 如果想退回到原本函数调用的地方, 则可使用return
向脚本传递参数
先shell脚本传递参数, 非常简单, 只需要在你执行命令的后面跟上即可, 看个例子:
#! /bin/sh
# test.sh
echo "$# parameters";
echo "$@";
echo "$0"
echo "$1"
输入:
test.sh 11 22
输出:
2 parameters
11 22
test.sh
11