Shell学习笔记 01 变量和参数传递

Posted by Huper on December 21, 2017

Git学完了,接下来想好好学下Shell编程。以后可能会经常在服务器上跑程序什么的,学好Shell的话效率会提高很多,因为用它来进行批处理很方便。之前在操作系统实验的时候接触过一些makefile的东西,但是感觉身边用这个东西的人很少,所以最后还是选择了学习Shell,这个系列就以bash为例,系统学习Shell。

最开始还是按照惯例,先来个Hello World。在、user/local/src下建立learningShell的目录,专门用来学习Shell。第一个程序test.sh

#!/bin/bash
echo "Hello World !"

$ sudo chmod +x ./test.sh

上方是脚本内容,添加#!/bin/bash说明使用bash脚本解释器,echo语句是打印。下方的chmod命令是修改文件属性,-x选项为指定文件添加脚本执行权限。然后就可以运行了,运行方式有:

Huper@Huper:/usr/local/src/learningShell$ ./test.sh
Hello World !
Huper@Huper:/usr/local/src/learningShell$ bash ./test.sh
Hello World !
Huper@Huper:/usr/local/src/learningShell$ sh ./test.sh
Hello World !
Huper@Huper:/usr/local/src/learningShell$ /bin/bash ./test.sh
Hello World !
Huper@Huper:/usr/local/src/learningShell$ test.sh
test.sh: command not found

这里你指定不指定解释器的完整路径都无所谓了,因为环境变量里有。但是注意不要直接使用tesh.sh,因为环境变量里没有你的当前路径的话,没办法直接找到你的脚本。准备的东西就这么多,下面直接进入今天要讲的额语法阶段。

变量

Shell脚本的变量在使用前要先定义,关于其分类和定义规则,可以总结如下:

分类:

  • 1) 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
  • 2) 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
  • 3) shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

规则:

  • 变量名和等号之间不能有空格
  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
  • 中间不能有空格,可以使用下划线(_)。
  • 不能使用标点符号。
  • 不能使用bash里的关键字(可用help命令查看保留关键字)。

举几个例子:

name='huper'
var1=123
var2=0.2

在使用变量的时候直接在变量名前面加上$就可以了,并且已定义的变量还可以重新定义:

Huper@Huper:/usr/local/src/learningShell$ name='huper'
Huper@Huper:/usr/local/src/learningShell$ echo $name  # = echo ${name}
huper
Huper@Huper:/usr/local/src/learningShell$ name='huper'
Huper@Huper:/usr/local/src/learningShell$ name='ProHuper'
Huper@Huper:/usr/local/src/learningShell$ echo $name
ProHuper

不同于python,Shell里可以直接声明一个变量是常量,其实也一直很好奇python为什么不支持显示定义常量:

Huper@Huper:/usr/local/src/learningShell$ readonly name="Huper"
Huper@Huper:/usr/local/src/learningShell$ name="ProHuper"
-bash: name: readonly variable

可以直接将某个变量删除,类似python里的del,但是只读变量是不能删除的:

Huper@Huper:/usr/local/src/learningShell$ unset name
-bash: unset: name: cannot unset: readonly variable
Huper@Huper:/usr/local/src/learningShell$ unset var

关于Shell里的字符串,支持单引号和双引号两种定义方式,单引号不支持使用转义字符,也不支持应用其他变量:

Huper@Huper:/usr/local/src/learningShell$ my_name=huper
Huper@Huper:/usr/local/src/learningShell$ str='my name is $my_name'
Huper@Huper:/usr/local/src/learningShell$ echo $str
my name is $my_name
Huper@Huper:/usr/local/src/learningShell$ str="my name is $my_name"
Huper@Huper:/usr/local/src/learningShell$ echo $str
my name is huper

可以对字符串进行各种各样的操作,方法如下:

#拼接
Huper@Huper:/usr/local/src/learningShell$ my_name='Huper'
Huper@Huper:/usr/local/src/learningShell$ append="Baby"
Huper@Huper:/usr/local/src/learningShell$ new_name="${my_name},${append}"
Huper@Huper:/usr/local/src/learningShell$ echo $new_name
Huper,Baby
Huper@Huper:/usr/local/src/learningShell$ new_name=""$my_name" is awesome!"
Huper@Huper:/usr/local/src/learningShell$ echo new_name
new_name
Huper@Huper:/usr/local/src/learningShell$ echo $new_name
Huper is awesome!
#获取长度
Huper@Huper:/usr/local/src/learningShell$ echo ${#new_name}
17
#获取子串
Huper@Huper:/usr/local/src/learningShell$ echo ${new_name:1:5}
uper
#查找子串
Huper@Huper:/usr/local/src/learningShell$ echo `expr index "$new_name" u`
2

当然这些操作用起来都比较鸡肋了,尤其是查找子串那个,我们完全不需要用shell来完成这些字符串操作。用Shell主要是来简化一些整合编译或者执行的工作,但是这些基本语法还是要学一下的。

参数传递

接下来要将的参数传递就很有用了,由于我们在执行脚本或者可执行文件的时候往往需要传递命令行参数,所以参数传递是Shell脚本里的一个重点。首先Shell脚本本身可以接受命令行参数,我们如何在脚本中引用这些参数呢?如下:

#!/bin/bash
#author:Huper

echo "Testing parametre..."
echo "First para: $0"
echo "Second para: $1"
echo "Third para: $2"
-----------------------------------------------------------
Huper@Huper:/usr/local/src/learningShell$ ./test.sh 1 2
Testing parametre...
First para: ./test.sh
Second para: 1
Third para: 2

可以看到使用$n来直接引用参数就行了,第0个参数还是默认为脚本名。这里要注意如果要引用的参数序号大于9就必须必须用{}括起来,比如${10}。还有一些特殊的符号意义如下:

符号 意义
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

我举个例子来测试一下$@$*的区别:

Huper@Huper:/usr/local/src/learningShell$ ./test.sh 1 2 3
1 2 3
------------------
1
2
3

这里for后面的分号不要漏了,奇怪的语法。。。