来来来,学习Shell的命令
Shell脚本是非常强的大一个脚本语言,但是不用会手生,所以在此记录Shell脚本的相应关键点,也做查字典用^_^
变量
变量定义
先来简单的看一下变量定义的规则
- 在
Shell
中,使用变量之前不需要事先声明,只是通过使用它们来创建它们; - 在默认情况下,所有变量都被看做是字符串,并以字符串来存储;
Shell
变量是区分大小写的;- 在赋值变量的时候等号两端不能有空格-_-
定义了变量之后,一定要加上$
符号才能使用
1 | #! /bin/bash |
输出的结果为
./test.sh: line 2: NAME: command not found
./test.sh: line 4: VAR4: command not found
VAR1
HELLO
MY AGE
关于
shell
脚本的执行:shell
基本一般是以.sh
为后缀,然后在*unix
系统下一般都是直接使用./[当前shell文件名]
的方式来执行,也可以使用全部经/[shell文件名]
的方式来执行,并且需要注意的是 被执行的shell
文件一定是有含有可执行权限了的,可以使用chmod
命令来修改
还有另一个点就是在调用变量的时候 ,如果在双引号中直接使用$name
任然可以识别,但是如果在单引号是就无法适用$name
的方式来调用变量
read读取输入值
这个功能就像java
中的readline
来读取,使用方法为1
2
3
4
5
6
7#! /bin/bash
echo "whats your name?"
read NAME #在这里读取输入值到NAME变量中 ,这里如果不输入会停留在屏幕上
echo "webcome back" $NAME
exit 0
可以看到熟悉的结果为
whats your name?
tom
webcome back tom
环境变量
Shell
脚本还提供能一些实用的环境变量
$HOME
:为当前用户所在的目录$PATH
:当前用户所能方法的PATH变量$#
:传递参数额个数 类似java
中的args.length
$$
:Shell
脚本的进程号,脚本程序通常会用它来生成一个唯一的临时文件。
1 | #! /bin/bash |
可以到看的结果是
yans-MacBook-Pro:Downloads yanyl$ ./hi.sh hello world
当前用户所在的目录为 /Users/yanyl
当前的执行目录为 /Users/yanyl/Downloads
当前用户所能访问的PATH为 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/yanyl/Program/apache-maven-3.2.5/bin:/Users/yanyl/Program/scala-2.10.4//bin
当前参数的参数个数为 2
当前Shell脚本的进程号为 43746
假如需要进入当前目录的父目录,可以使用
$(dirname $(pwd))
参数变量
刚刚看到可以使用read
关键字可以来读取输入变量,但是我们可能更加常用的是参数变量,也就是$#
的个数,它的规则如下
$#
表示参数变量的个数$0
表示当前的脚本名称$1,$2…$n
表示依次能读取到的变量 但是如果参数变量不够,$i
会被赋值为空
1 | #! /bin/bash |
可以看到的结果为
yans-MacBook-Pro:Downloads yanyl$ ./hi.sh hello world
当前输入的参数变量的长度为 2
当前执行的Shell脚本为 ./hi.sh
当前输入的第一个参数为 hello
当前输入的第二个参数为 world
当前的输入的第三个参数为
可以看到在Shell
脚本中去读取参数变量还是很方便的,这样配合下面的条件判断以及循环就可以做很多事情了
读取返回码
一般的程序/命令在执行结束时都会返回一个 返回码,比如
java
的system.exit(-1)
python
的sys.exit(-1)
- 还有上面
Shell
脚本中的最后一行exit 0
如果你不显式指定返回码,一般默认为0,表示正常退出,但是有时候显式的指定返回码是一个好习惯哦
这些程序在Shell
中执行的,可以使用$?
来读取上一个程序执行下来的脚本码
1 | #! /bin/bash |
可以看到正确的结果为
28494656 .
du -s的返回码为 0
./hi.sh: line 6: duu: command not found
duu -s的返回码为 127
返回码配上
if
判断,就可以使用shell
脚本自由得在各个语言以及命令中穿梭啦^_^
数学运算
在上一小节中说道,Shell
中变量一般都是当字符串来处理,那我遇到数字运算该咋办呢??
可以先看1
2
3
4
5
6
7
8#! /bin/bash
a=1+2
b=$a+3
echo $a
echo $b
exit 0
结果却看到
1+2
1+2+3
那在Shell
中解决这个问题大概有这么几种方法
let关键字
1 | #! /bin/bash |
输出的结果为
3
6
这个关键词大致需要注意以下几个点:
let
只支持整数运算- 当
let
后面的运算部分有bash
关键字时,需加双引号 - 幂次方可以使用**符号
使用(())
1 | #! /bin/bash |
结果还是正确的
3
6
(())
的用法与let
完全相同
使用$[]
上面的效果需要这么写1
2a=$[1+2]
b=$[$a+3]
其余与上面两种限制大致相同
使用expr
关于这个方式是这么写的1
2a=`expr 1 + 2`
b=`expr $a \* 3` #需要转义
需要额外注意的有:
- 运算符两端需要加空格 一定要记住。。。很容易失误
- 对于
|、&、<、<=、>=、>、*
运算符号需要加上\
进行转义
使用bc
这个终于是可以用于浮点数的运算了1
2
3
4
5
6
7
8#! /bin/bash
a=3.1415926
b=`echo "$a*2"|bc`
echo $a
echo $b
exit 0
可以看到结果
3.1415926
6.2831852
据说这里还有一个scale
来设置精度,但是我设置了感觉木有效果-_-
条件判断
if 语法
在Shell
脚本中有两种书写if
判断的语法
使用
test
关键字1
2
3
4
5
6
7
8
9
10
11#! /bin/bash
# if test expression1 operation expression2
if test 5 -gt 4; #这个最后的结尾可以加上:或者;
then
echo "ok,5>4"
else
echo "oh,no"
fi #这个结束符号必须得加
exit 0输出为
ok,5>4
使用
[
和]
关键字1
2
3
4
5
6
7
8
9
10
11#! /bin/bash
# if [ expression1 operation expression2 ]
if [ 5 -lt 4 ]; #注意[和]两端必须留空格 同时表达式两端都需要有空格
then
echo "ok,5>4"
else
echo "oh,no"
fi
exit 0输出为
oh,no
如果还更加复杂的判断你可以使用
elif
继续增加条件表达式,但是别忘了加then
哦
判断表达式
在Shell
中有三种判断表达式
字符串比较
字符串比较 | 结果 |
---|---|
string1 = string2 |
如果两个字符串相同,也可用== 结果就为真 |
string1 != string2 |
如果两个字符串不同,结果就为真 |
-n string |
如果字符串不为空,则结果为真 |
-z string |
如果字符串为一个空串(null ),则结果为真 |
这里需要注意下,
-n
和-z string
比较时必须用双引号(“”)将变量引起来
1 | #! /bin/bash |
结果为
exists
null
算术比较
算术比较 | 结果 |
---|---|
expression1 -eq expression2 |
如果两个表达式相等,则结果为真 |
expression1 -ne expression2 |
如果两个表达式不等,则结果为真 |
expression1 -gt expression2 |
如果expression1 大于expression2 ,则为真 |
expression1 -ge expression2 |
如果expression1 大于等于expression2 ,则为真 |
expression1 -lt expression2 |
如果expression1 小于expression2 ,则为真 |
expression1 -le expression2 |
如果expression1 小于等于expression2 ,则为真 |
!expression |
表达式为假,则结果就为真;反之亦然 |
关于上面比较符号的快速记法如下:
eq=equal
,gt=great than
,lt=less than
,然后组合拼凑即可,如果觉得这样还是很难记,就可以像我一样,将这些符号记录下来,需要的时候来查表-_-
文件条件测试
文件条件测试 | 结果 |
---|---|
-d file |
如果文件是一个目录,则为真 |
-f file |
如果文件是一个普通文件,则为真;也可以用来测试文件是否存在 |
-r file |
如果文件可读,则结果为真 |
-s file |
如果文件大小不为0,则结果为真 |
-w file |
如果文件可写,则结果为真 |
-x file |
如果文件可执行,则结果为真 |
这,真的是一个利民的测试
循环结构
for 循环
先来看一种经典C
语法版的for
1
2
3
4
5
6
7#! /bin/bash
for ((i=0;i<5;i++))
do
echo $i
done
exit 0
看输出,
0
1
2
3
4
还支持在外部控制步长1
2
3
4
5
6
7
8#! /bin/bash
for ((i=0;i<5;))
do
echo $i
i=$[$i+2]
done
exit 0
0
2
4
是不是感觉基本功能都有呀,就是写某些东西写起来奇怪点
是不是有一种莫名的熟悉感
另一种就是类似foreach
的情况了,他的格式是这样的1
2
3
4for variable in values
do
statements
done
其中values
可能有的情况为:
使用
linux
命令输出的行作为迭代的输入:ls
,seq
,cat
之类均可,其实就可以完成很强大的文件读取功能1
2
3
4
5
6
7#! /bin/bash
for i in `head -n 5 words.dit`;do #words.dit 这是一个通用词表 每行一个词
echo $i
done
exit 0可以看到通用词典中前5个词
阿 阿巴丹 阿巴岛 阿巴鸟 阿巴伊达
使用
$*
可以来表示遍历传入的参数列表1
2
3
4
5
6
7#! /bin/bash
for i in $*;do
echo $i
done
exit 0来看个结果
yans-MacBook-Pro:Downloads yanyl$ ./hi.sh my name is tom my name is tom
还可以使用带空格的字符串 来进行按空格分隔输出
1
2
3
4
5
6
7
8#! /bin/bash
a="yello red green"
for i in $a;do
echo $i
done
exit 0这样在一定程度上可以看成一个简易的数组
这里需要注意的是包含条件以及循环逻辑是双重括号,以及开始结果的do
和Done
while 循环
另一个常用的就是while
循环了
他的结构是1
2
3
4while condition
do
statements
done
这个也是蛮好理解的,可以来看一个demo1
2
3
4
5
6
7
8
9
10
11
12
13#! /bin/bash
echo "please ent your password:"
read pwd
while [ "$pwd"x != "root"x ] #这里加x是为了防止啥也不输入直接回车产生的报错
do
echo "error,please try again:"
read pwd
done
echo "welcome here"
exit 0
看一下结果
please ent your password:
sha
error,please try again:
error,please try again:
root
welcome here
很有意思的一个哈~
until语句
这个语句与while
的结构完全一样,只是使用了until
关键字来代替了while
,然后在条件为true
的时候停止,正好与while
相反
函数
Shell
这么叼,能没有函数吗1
2
3
4[function] functon_name()
{
statements
}
上面是定义函数的结构,大致有以下几个要点
- 前面的
function
关键字可有可无,不过感觉还是加上去比较好,这样在代码里面比较好辨识 - 函数名后面的括号中不能带参数 取的参数是用过
$1,$2…$n
这样的方式来取的 - 调用的时候直接写函数名 不需要加括号
- 如果想传递参数的话 直接在调用后来加上参数列表 用空格隔开 (就是
Shell
的传参一样) - 使用
local
关键字来定义函数体里面的局部变量 - 所以在函数调用必须在函数定义之后
先看一个小的demo1
2
3
4
5
6
7
8
9
10#! /bin/bash
function sayhi()
{
echo hi $1
}
sayhi tom #前面的sayhi是函数的调用 后面的tom是传参
exit 0
可以看到输出
hi tom
函数的返回值
关于Shell
的返回值方式有两种
输出给主程序,他的结构为:
1
2
3
4
5
6function function_name()
{
echo $something #通过输出的方式来返回值
}
a=`function_name` 这种方式接收返回值看到的demo可以是这样的
1
2
3
4
5
6
7
8
9
10
11
12Press ENTER or type command to continue
#! /bin/bash
function sum()
{
echo $[$1+$2]
}
a=`sum 1 2`
echo the sum is $a
exit 0最终输出结果为
the sum is 3
使用
return
作为返回码来返回值1
2
3
4
5
6
7function function_name()
{
return $ret #这里进行返回码的返回
}
function_name
$? #在这里接收返回值一样再来一个demo
1
2
3
4
5
6
7
8
9
10
11#! /bin/bash
function sum()
{
return $[$1+$2]
}
sum 1 2
echo the sum is $?
exit 0可以看到输出为
the sum is 3
case语句
这里的case
的与传统的switch
有点像,但是又像scala
中的match
模式匹配的强大,
他的结构是这样的1
2
3
4
5case variable in
pattern [ | pattern] ...) statements;;
pattern [ | pattern] ...) statements;;
...
esac
来看这个强大的demo1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#! /bin/bash
function match()
{
case $1 in
root ) echo this is password ;;
h* ) echo hi $1 ;; #使用通配符
yes | YES ) echo agree with me ;; #可以进行或操作
* ) echo everything is here;; #你可以理解为switch中的default
esac
}
match root
match hello
match YES
match Yes
exit 0
来看一下结果
this is password
hi hello
agree with me
everything is here
注意,这里一旦匹配中了一个之后就马上会停止匹配
外部命令/文件/语言的调用
Shell
的另一个强大之处就是可以无缝的和外部的命令,文件,语言结合,去调用组织他们
- 外部命令:一般情况下可以直接写外部命令,如果要赋值的话得使用
``
括起来 - 外部文件:比如资源配置文件,profile文件之类的,可以直接使用
source
关键字的来执行 - 外部语言:比如
java,python
可以直接使用他们的java
调用jar,java
文件,也可以直接使用关键字来执行
python
文件
总结
Shell
很好很强大,得学习!!!- 注意变量的字符串格式以及需要数学运算时的语法
- 注意变量赋值时等号两端一定不能有空格以及再取值时一定要加
$
- 平常的控制结束符号别忘了,比如
fi,doen,esac
等 - 忘了的时候来查查这个文件
参考
本作品采用[知识共享署名-非商业性使用-相同方式共享 2.5]中国大陆许可协议进行许可,我的博客欢迎复制共享,但在同时,希望保留我的署名权