Shell 简介
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。
Shell 脚本
Shell 脚本(shell script),是一种为 shell 编写的脚本程序。
业界所说的 shell 通常都是指 shell 脚本,但读者朋友要知道,shell 和 shell script 是两个不同的概念。
由于习惯的原因,简洁起见,本文出现的 “shell编程” 都是指 shell 脚本编程,不是指开发 shell 自身。
Shell 脚本编译器
打开文本编辑器(可以使用 vi/vim 命令来创建文件),新建一个文件 test.sh,扩展名为 sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用 php 写 shell 脚本,扩展名就用 php 好了。
输入一些代码,第一行一般是这样:
#!/bin/bash
echo "Hello World !"
#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。
echo 命令用于向窗口输出文本。
运行 Shell 脚本有两种方法:
1、作为可执行程序
将上面的代码保存为 test.sh,并 cd 到相应目录:
[root@ chenc01 ~]# chmod +x ./test.sh # 使脚本具有执行权限
[root@ chenc01 ~]# ./test.sh # 执行脚本
# 注意,一定要写成 ./test.sh,而不是 test.sh,运行其它二进制的程序也一样,直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当前目录找。
2、作为解释器参数
这种运行方式是,直接运行解释器,其参数就是 shell 脚本的文件名,如:
[root@ chenc01 ~]# /bin/sh test.sh
[root@ chenc01 ~]# /bin/php test.php
Shell变量
1、系统变量
在命令行提示符直接执行env、set查看系统或环境变量。env显示用户环境变量,set显示Shell预先定义好的变量以及用户变量。可以通过export导出成用户变量。
一些写Shell脚本时常用的系统变量:
$Shell | 默认Shell |
---|---|
$HOME | 当前用户家目录 |
$IFS | 内部字段分隔符 |
$LANG | 默认语言 |
$PATH | 默认可执行程序路径 |
$PWD | 当前目录 |
$UID | 当前用户ID |
$USER | 当前用户 |
$HISTSIZE | 历史命令大小,可通过HISTTIMEFORMAT变量设置命令执行时间 |
$RANDOM | 随机生成一个0至32767的整数 |
$HOSTNAME | 主机名 |
2、普通变量与临时环境变量
普通变量定义:VAR=value
临时环境变量定义:export VAR=value
变量引用:$VAR
下面看下他们之间区别:
Shell进程的环境变量作用域是Shell进程,当export导入到系统变量时,则作用域是Shell进程及其Shell子进程。
[root@ chenc01 ~]# ps axjf | grep pts
1090 1129 1129 1129 ? -1 Ss 0 0:00 \_ sshd: root@pts/0
1129 1131 1131 1131 pts/0 1131 Ss+ 0 0:00 | \_ -bash
1090 1188 1188 1188 ? -1 Ss 0 0:00 \_ sshd: root@pts/1
1188 1190 1190 1190 pts/1 1235 Ss 0 0:00 \_ -bash
1190 1235 1235 1190 pts/1 1235 R+ 0 0:00 \_ ps axjf
1190 1236 1235 1190 pts/1 1235 S+ 0 0:00 \_ grep --color=auto pts
[root@ chenc01 ~]# echo $$
1190
[root@ chenc01 ~]# VAR=123
[root@ chenc01 ~]# echo $VAR
123
[root@ chenc01 ~]# bash
[root@chenc01 ~]# echo $$
1237
[root@chenc01 ~]# ps axjf | grep pts
1090 1129 1129 1129 ? -1 Ss 0 0:00 \_ sshd: root@pts/0
1129 1131 1131 1131 pts/0 1131 Ss+ 0 0:00 | \_ -bash
1090 1188 1188 1188 ? -1 Ss 0 0:00 \_ sshd: root@pts/1
1188 1190 1190 1190 pts/1 1246 Ss 0 0:00 \_ -bash
1190 1237 1237 1190 pts/1 1246 S 0 0:00 \_ bash
1237 1246 1246 1190 pts/1 1246 R+ 0 0:00 \_ ps axjf
1237 1247 1246 1190 pts/1 1246 S+ 0 0:00 \_ grep pts
[root@chenc01 ~]# echo $VAR
[root@chenc01 ~]# exit
exit
[root@ chenc01 ~]# echo $VAR
123
[root@ chenc01 ~]# export VAR
[root@ chenc01 ~]# bash
[root@chenc01 ~]# echo $$
1248
[root@chenc01 ~]# ps axjf | grep pts
1090 1129 1129 1129 ? -1 Ss 0 0:00 \_ sshd: root@pts/0
1129 1131 1131 1131 pts/0 1131 Ss+ 0 0:00 | \_ -bash
1090 1188 1188 1188 ? -1 Ss 0 0:00 \_ sshd: root@pts/1
1188 1190 1190 1190 pts/1 1257 Ss 0 0:00 \_ -bash
1190 1248 1248 1190 pts/1 1257 S 0 0:00 \_ bash
1248 1257 1257 1190 pts/1 1257 R+ 0 0:00 \_ ps axjf
1248 1258 1257 1190 pts/1 1257 S+ 0 0:00 \_ grep pts
[root@chenc01 ~]# echo $VAR
123
[root@chenc01 ~]# ps -ef|grep ssh
root 1090 1 0 11:18 ? 00:00:00 /usr/sbin/sshd
ps axjf输出的第一列是PPID(父进程ID),第二列是PID(子进程ID)
当SSH连接Shell时,当前终端PPID(-bash)是sshd守护程序的PID(root@pts/0),因此在当前终端下的所有进程的PPID都是-bash的PID,比如执行命令、运行脚本。
所以当在-bash下设置的变量,只在-bash进程下有效,而-bash下的子进程bash是无效的,当export后才有效。
进一步说明:再重新连接SSH,去除上面定义的变量测试下
[root@ chenc01 ~]# ps -axjf | grep pts
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
1090 1129 1129 1129 ? -1 Ss 0 0:00 \_ sshd: root@pts/0
1129 1131 1131 1131 pts/0 1131 Ss+ 0 0:00 | \_ -bash
1090 1188 1188 1188 ? -1 Ss 0 0:00 \_ sshd: root@pts/1
1188 1190 1190 1190 pts/1 1278 Ss 0 0:00 \_ -bash
1190 1278 1278 1190 pts/1 1278 R+ 0 0:00 \_ ps -axjf
1190 1279 1278 1190 pts/1 1278 S+ 0 0:00 \_ grep --color=auto pts
[root@ chenc01 ~]# echo $$
1190
[root@ chenc01 ~]# VAR=123
[root@ chenc01 ~]# vim test.sh
[root@ chenc01 ~]# cat test.sh
#!/bin/bash
ps -axjf | grep pts
echo $$
echo $VAR
[root@ chenc01 ~]# bash test.sh
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
1090 1129 1129 1129 ? -1 Ss 0 0:00 \_ sshd: root@pts/0
1129 1131 1131 1131 pts/0 1131 Ss+ 0 0:00 | \_ -bash
1090 1188 1188 1188 ? -1 Ss 0 0:00 \_ sshd: root@pts/1
1188 1190 1190 1190 pts/1 1282 Ss 0 0:00 \_ -bash
1190 1282 1282 1190 pts/1 1282 S+ 0 0:00 \_ bash test.sh
1282 1283 1282 1190 pts/1 1282 R+ 0 0:00 \_ ps -axjf
1282 1284 1282 1190 pts/1 1282 S+ 0 0:00 \_ grep pts
1282
123
[root@ chenc01 ~]# export VAR
[root@ chenc01 ~]# bash test.sh
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
1090 1129 1129 1129 ? -1 Ss 0 0:00 \_ sshd: root@pts/0
1129 1131 1131 1131 pts/0 1131 Ss+ 0 0:00 | \_ -bash
1090 1188 1188 1188 ? -1 Ss 0 0:00 \_ sshd: root@pts/1
1188 1190 1190 1190 pts/1 1285 Ss 0 0:00 \_ -bash
1190 1285 1285 1190 pts/1 1285 S+ 0 0:00 \_ bash test.sh
1285 1286 1285 1190 pts/1 1285 R+ 0 0:00 \_ ps -axjf
1285 1287 1285 1190 pts/1 1285 S+ 0 0:00 \_ grep pts
1285
123
所以在当前shell定义的变量一定要export,否则在写脚本时,会引用不到。
还需要注意的是退出终端后,所有用户定义的变量都会清除。
在/etc/profile下定义的变量就是这个原理,后面有章节会讲解Linux常用变量文件。
3、位置变量
位置变量指的是函数或脚本后跟的第n个参数。
1 − 1- 1−n,需要注意的是从第10个开始要用花括号调用,例如${10}
shift可对位置变量控制,例如:
#!/bin/bash
echo "1: $1"
shift
echo "2: $2"
shift
echo "3: $3"
[root@ chenc01 ~]# bash a.sh a b c
1: a
2: c
3:
每执行一次shift命令,位置变量个数就会减一,而变量值则提前一位。shift n,可设置向前移动n位。
4、特殊变量
$0 | 脚本自身名字 |
---|---|
$? | 返回上一条命令是否执行成功,0为执行成功,非0则为执行失败 |
$# | 位置参数总数 |
$* | 所有的位置参数被看做一个字符串 |
$@ | 每个位置参数被看做独立的字符串 |
$$ | 当前进程PID |
$! | 上一条运行后台进程的PID |
变量引用
赋值运算符 | 示例 |
---|---|
= | 变量赋值 |
+= | 两个变量相加 |
1、自定义变量与引用
[root@ chenc01 ~]# VAR=123
[root@ chenc01 ~]# echo $VAR
123
[root@ chenc01 ~]# VAR+=321
[root@ chenc01 ~]# echo $VAR
123321
Shell中所有变量引用使用$符,后跟变量名。
有时个别特殊字符会影响正常引用,那么需要使用${VAR},例如:
[root@ chenc01 ~]# VAR=123
[root@ chenc01 ~]# echo $VAR
123
[root@ chenc01 ~]# echo $VAR_ # Shell允许VAR_为变量名,所以此引用认为这是一个有效的变量名,故此返回空
空
[root@ chenc01 ~]# echo ${VAR}
123
还有时候变量名与其他字符串紧碍着,也会误认为是整个变量:
[root@ chenc01 ~]# echo $VAR321
[root@ chenc01 ~]# echo ${VAR}321
123321
2、将命令结果作为变量值
[root@ chenc01 ~]# VAR=`echo 123`
[root@ chenc01 ~]# echo $VAR
123
[root@ chenc01 ~]# VAR=$(echo 123)
[root@ chenc01 ~]# echo $VAR
123
这里的反撇号等效于$(),都是用于执行Shell命令。
双引号和单引号
在变量赋值时,如果值有空格,Shell会把空格后面的字符串解释为命令:
[root@ chenc01 ~]# VAR=1 2 3
-bash: 2: command not found
[root@ chenc01 ~]# VAR="1 2 3"
[root@ chenc01 ~]# echo $VAR
1 2 3
[root@ chenc01 ~]# VAR='1 2 3'
[root@ chenc01 ~]# echo $VAR
1 2 3
看不出什么区别,再举个说明:
[root@ chenc01 ~]# N=3
[root@ chenc01 ~]# VAR="1 2 $N"
[root@ chenc01 ~]# echo $VAR
1 2 3
[root@ chenc01 ~]# VAR='1 2 $N'
[root@ chenc01 ~]# echo $VAR
1 2 $N
单引号是告诉Shell忽略特殊字符,而双引号则解释特殊符号原有的意义,比如$、!。
注释
Shell注释也很简单,只要在每行前面加个#号,即表示Shell忽略解释。