平常经常写一些shell脚本,来执行一些自动化任务,比如打包、连接等,记录下常用到的语法和命令。
1. 格式和运行
格式是指开头第一行
1 |
其中的#!叫shebang,也叫hashbang,后面跟着的这个脚本解释器的绝对路径,我的电脑上用的是bash。运行的时候直接使用sh后面跟上脚本的名字即可,如果给脚本文件加上可执行的权限,用./也可执行,如果不写第一行的话,你会发现也可以执行,如果不光是给自己用,建议还是要写上的。
2. 获取参数
有时一些参数是不固定的,需要每次运行的时候传入。方式有两种,一种是直接在后面写参数的值,如
1 | sh command.sh arg1 arg2 arg3 |
这种方式在读取时,是凭借参数的位置顺序读取的,$0为command.sh,$1为参数一arg1,$2为参数二arg2,依次类推。这种方式方便传入和读取,但是受到位置的限制,有时可能只需要传入参数二,而参数一使用缺省值便可,这时使用-argName的方式会更方便,按需指定并传入,如
1 | sh command.sh -a argA -b argB -c argC |
读取时,要这样,getopts,即get options,获取所有的可选参数,通过循环读取每个参数的值
1 | while getopts "a:b:c:" opt |
3. 条件判断
判断是脚本中最常见的命令语句了,基本语法为
1 | if [ command ]; then |
其中的command有很多,按类型可以分为:判断文件/目录、判断字符串和判断数值大小,举几个常用的
1 | # 文件/目录 |
4. 服务器
ssh连接服务器,如果已经把自己的公钥id_rsa.pub添加到了服务器的authorized_keys里,那么可以直接连接,就可以了
1 | ssh username@server-ip |
如果没有添加,则需要指定服务器的私钥的pem文件作为参数
1 | ssh -i identification.pem username@server-ip |
原理很简单,一是服务器信任了自己的公钥,二是拿着服务器的私钥的pem,pem的获取方式为
1 | # 服务器生成公钥匙私钥对,这会得到一个私钥private,和公钥private.pub |
当然,光连上服务器不是目的,这些如果写到shell脚本里会中断执行,而我的目的是在服务器上执行命令,连到服务器只是前提,若执行命令,如下
1 | ssh username@server-ip "[command]" |
有时还会需要在本地和远程之间传递文件命令,从本地到远程如下(远程到本地就反过来),注意server-ip后面的冒号,
1 | scp [options] /local/source/file/path user@server-ip:/path/to/destination |
服务器的IP地址经常会变,每次通过ssh连接一个不存在于.ssh/known_hosts中的ip时,都会弹出一个警告,问是否要将这个正在连接的主机添加到已知设备中,中断自动化脚本的执行。
在这里,都要连接登陆了,所以肯定是要加的,可以通过指定一个参数,让其自动添加,而不中断自动化流程
1 | ssh -o StrictHostKeyChecking=no -i identificaiton.pem username@host |
5. 编译shell脚本
有时写完一个shell脚本,需要给别人使用,但是又不想让别人看到里面的代码,可能因为里面有重要数据,也可能没有为什么,就是不想,这时可以把shell脚本编译成可执行文件,这样一来,既可以执行,但又无法查看了。一共有两种方式,一是系统自带的gzexe,二是使用shc命令,
1 | gzexe command.sh |
这会在目录下生成两个文件,一个是command.sh,另个是command.sh~,前者是压缩之后的,带~的是原文件,打开压缩之后的文件,可以发现里面有不少内容还是可以看懂的,尽管多数都不沾边了。
1 | shc -f command.sh |
这也会在目录下生成两个文件,一个是command.sh.x,这是可执行文件,另个是command.sh.x.c,这是个c文件,打开可执行文件后,会发现完全看不懂,都是乱码,推测可能先生成了c文件,然后再将c编译成二进制可执行文件。这是shc的github地址
6. 脚本里调用其他脚本
当有多个shell文件,其中相互调用的时候,要用source命令
1 | ! /bin/bash |
虽然写在多个shell文件里,但是因为运行在同一个shell会话中,所以变量是可以共用的,也就是first里声明的变量,在other里可以读取,我一般用这种方式来处理脚本的参数,当一个脚本需要多个参数时,就把这些参数都单独写在一个shell里,然后再最后通过source调用真正的执行文件就好。
7. $相关的变量说明
变量 | 含义 |
---|---|
$0 | 脚本的名字,即文件名 |
$n(n ≥ 1) | 参数名字 |
$# | 传给脚本的参数个数 |
$* | 传来的所有参数 |
$@ | 传来的所有参数 |
$? | 上个命令的退出值 |
$$ | shell进程ID |
其中,$@
和$*
,没有引号包围时,二者完全一样,当有双引号包围时,$@
依然无变化,而$*
则是把所有参数合并成了一个变量
1 | sh test.sh p1 p2 p3 |