目录:
变量 | 含义 |
---|---|
$0 | 脚本名字 |
$1 | 位置参数 #1 |
$2 - $9 | 位置参数 #2 - #9 |
${10} | 位置参数 #10 |
$# | 位置参数的个数 |
"$*" | 所有的位置参数(作为单个字符串) * |
"$@" | 所有的位置参数(每个都作为独立的字符串) |
${#*} | 传递到脚本中的命令行参数的个数 |
${#@} | 传递到脚本中的命令行参数的个数 |
$? | 返回值 |
$$ | 脚本的进程ID(PID) |
$- | 传递到脚本中的标志(使用set) |
$_ | 之前命令的最后一个参数 |
$! | 运行在后台的最后一个作业的进程ID(PID) |
* 必须被引用起来, 否则默认为"$@
".
测试操作: 二元比较
操作 | 描述 | ----- | 操作 | 描述 |
---|---|---|---|---|
算术比较 | 字符串比较 | |||
-eq | 等于 | = | 等于 | |
== | 等于 | |||
-ne | 不等于 | != | 不等于 | |
-lt | 小于 | \< | 小于 (ASCII) * | |
-le | 小于等于 | |||
-gt | 大于 | \> | 大于 (ASCII) * | |
-ge | 大于等于 | |||
-z | 字符串为空 | |||
-n | 字符串不为空 | |||
算术比较 | 双括号(( ... ))结构 | |||
> | 大于 | |||
>= | 大于等于 | |||
< | 小于 | |||
<= | 小于等于 |
* 如果在双中括号 [[ ... ]] 测试结构中使用的话, 那么就不需要使用转义符\了.
文件类型的测试操作
操作 | 测试条件 | 操作 | 测试条件 |
---|---|---|---|
-e | 文件是否存在 | -s | 文件大小不为0 |
-f | 是一个标准文件 | ||
-d | 是一个目录 | -r | 文件具有读权限 |
-h | 文件是一个符号链接 | -w | 文件具有写权限 |
-L | 文件是一个符号链接 | -x | 文件具有执行权限 |
-b | 文件是一个块设备 | ||
-c | 文件是一个字符设备 | -g | 设置了sgid标记 |
-p | 文件是一个管道 | -u | 设置了suid标记 |
-S | 文件是一个socket | -k | 设置了"粘贴位" |
-t | 文件与一个终端相关联 | ||
-N | 从这个文件最后一次被读取之后, 它被修改过 | F1 -nt F2 | 文件F1比文件F2新 * |
-O | 这个文件的宿主是你 | F1 -ot F2 | 文件F1比文件F2旧 * |
-G | 文件的组id与你所属的组相同 | F1 -ef F2 | 文件F1和文件F2都是同一个文件的硬链接 * |
! | "非" (反转上边的测试结果) |
* 二元操作符(需要两个操作数).
参数替换和扩展
表达式 | 含义 |
---|---|
${var} | 变量var 的值, 与$var 相同 |
${var-DEFAULT} | 如果var 没有被声明, 那么就以$DEFAULT 作为其值 * |
${var:-DEFAULT} | 如果var 没有被声明, 或者其值为空, 那么就以$DEFAULT 作为其值 * |
${var=DEFAULT} | 如果var 没有被声明, 那么就以$DEFAULT 作为其值 * |
${var:=DEFAULT} | 如果var 没有被声明, 或者其值为空, 那么就以$DEFAULT 作为其值 * |
${var+OTHER} | 如果var 声明了, 那么其值就是$OTHER , 否则就为null字符串 |
${var:+OTHER} | 如果var 被设置了, 那么其值就是$OTHER , 否则就为null字符串 |
${var?ERR_MSG} | 如果var 没被声明, 那么就打印$ERR_MSG * |
${var:?ERR_MSG} | 如果var 没被设置, 那么就打印$ERR_MSG * |
${!varprefix*} | 匹配之前所有以varprefix 开头进行声明的变量 |
${!varprefix@} | 匹配之前所有以varprefix 开头进行声明的变量 |
* 当然, 如果变量var
已经被设置的话, 那么其值就是$var
.
字符串操作
表达式 | 含义 |
---|---|
${#string} | $string 的长度 |
${string:position} | 在$string 中, 从位置$position 开始提取子串 |
${string:position:length} | 在$string 中, 从位置$position 开始提取长度为$length 的子串 |
${string#substring} | 从变量$string 的开头, 删除最短匹配$substring 的子串 |
${string##substring} | 从变量$string 的开头, 删除最长匹配$substring 的子串 |
${string%substring} | 从变量$string 的结尾, 删除最短匹配$substring 的子串 |
${string%%substring} | 从变量$string 的结尾, 删除最长匹配$substring 的子串 |
${string/substring/replacement} | 使用$replacement , 来代替第一个匹配的$substring |
${string//substring/replacement} | 使用$replacement , 代替所有匹配的$substring |
${string/#substring/replacement} | 如果$string 的前缀匹配$substring , 那么就用$replacement 来代替匹配到的$substring |
${string/%substring/replacement} | 如果$string 的后缀匹配$substring , 那么就用$replacement 来代替匹配到的$substring |
expr match "$string" '$substring' | 匹配$string 开头的$substring *的长度 |
expr "$string" : '$substring' | 匹配$string 开头的$substring *的长度 |
expr index "$string" $substring | 在$string 中匹配到的$substring 的第一个字符出现的位置 |
expr substr $string $position $length | 在$string 中从位置$position 开始提取长度为$length 的子串 |
expr match "$string" '\($substring\)' | 从$string 的开头位置提取$substring * |
expr "$string" : '\($substring\)' | 从$string 的开头位置提取$substring * |
expr match "$string" '.*\($substring\)' | 从$string 的结尾提取$substring * |
expr "$string" : '.*\($substring\)' | 从$string 的结尾提取$substring * |
* $substring
是一个正则表达式.
一些结构的汇总
表达式 | 解释 |
---|---|
中括号 | |
if [ CONDITION ] | 测试结构 |
if [[ CONDITION ]] | 扩展的测试结构 |
Array[1]=element1 | 数组初始化 |
[a-z] | 正则表达式的字符范围 |
大括号 | |
${variable} | 参数替换 |
${!variable} | 间接变量引用 |
{ command1; command2; . . . commandN; } | 代码块 |
{string1,string2,string3,...} | 大括号扩展 |
圆括号 | |
( command1; command2 ) | 子shell中执行的命令组 |
Array=(element1 element2 element3) | 数组初始化 |
result=$(COMMAND) | 在子shell中执行命令, 并将结果赋值给变量 |
>(COMMAND) | 进程替换 |
<(COMMAND) | 进程替换 |
双圆括号 | |
(( var = 78 )) | 整型运算 |
var=$(( 20 + 5 )) | 整型运算, 并将结果赋值给变量 |
引号 | |
"$variable" | "弱"引用 |
'string' | "强"引用 |
后置引用 | |
result=`COMMAND` | 在子shell中运行命令, 并将结果赋值给变量 |
退出码的值 | 含义 | 例子 | 注释 |
---|---|---|---|
1 | 通用错误 | let "var1 = 1/0" | 各种各样的错误都可能使用这个退出码, 比如"除0错误" |
2 | shell内建命令使用错误(Bash文档上有说明) | 很少看到, 通常情况下退出码都为1 | |
126 | 命令调用不能执行 | 程序或命令的权限是不可执行的 | |
127 | "command not found" | 估计是$PATH 不对, 或者是拼写错误 | |
128 | exit的参数错误 | exit 3.14159 | exit只能以整数作为参数, 范围是0 - 255(见脚注) |
128+n | 信号"n"的致命错误 | kill -9 脚本的$PPID | $? 返回137(128 + 9) |
130 | 用Control-C来结束脚本 | Control-C是信号2的致命错误, (130 = 128 + 2, 见上边) | |
255* | 超出范围的退出状态 | exit -1 | exit命令只能够接受范围是0 - 255的整数作为参数 |
启动文件
这些文件包含别名和环境变量, 正是这些别名和环境变量才使得Bash可以作为一个用户shell来运行, 当系统初始化之后, 这些别名和变量也可被其他的的Bash脚本调用.
/etc/profile
系统范围的默认值, 大部分用来设置环境(所有的Bourne类型的shell, 而不仅仅是Bash[1])
/etc/bashrc
特定于Bash的, 系统范围函数与别名
$HOME
/.bash_profile用户定义的, 环境默认设置, 在每个用户的home目录下都可找到(本地副本保存在/etc/profile)
$HOME
/.bashrc用户定义的Bash初始化文件, 可以在每个用户的home目录下找到(本地副本保存在/etc/bashrc). 只有交互式的shell和用户脚本才会读取这个文件.
登出文件
$HOME
/.bash_logout用户定义的指令文件, 在每个用户的home目录下找到. 在登出(Bash)shell的时候, 这个文件中的命令就会得到执行.
注意事项
[1] | 不能应用于csh, tcsh, 或那些与经典Bourne shell无关的shell(也就是说那些不是派生自sh的shell). |
每位系统管理员或者是编写系统管理脚本的人员都应该对这些系统目录非常熟悉.
/bin
二进制(可执行文件). 基本的系统程序和工具(比如bash).
/usr/bin [1]
更多的系统二进制可执行文件.
/usr/local/bin
一些局部于特定机器的杂项二进制可执行文件.
/sbin
系统二进制可执行文件. 基本的系统管理程序和工具(比如fsck).
/usr/sbin
更多的系统管理程序和工具.
/etc
其他. 系统范围的配置脚本.
其中比较有趣的文件是/etc/fstab(文件系统表), /etc/mtab(挂载文件系统表), 还有文件/etc/inittab.
/etc/rc.d
启动脚本, 适用于红帽及其派生的Linux发行版.
/usr/share/doc
安装包的文档.
/usr/man
系统范围的man页.
/dev
设备目录. 物理设备和虚拟设备的入口(但不是挂载点)
/proc
进程目录. 包含关于运行进程和内核参数的统计信息与其他信息
/sys
系统范围的设备目录. 包含关于设备和设备名称的统计信息与其他信息. 这是在Linux 2.6.X内核版本上新添加的目录.
/mnt
挂载. 挂载硬驱动分区的目录, 比如/mnt/dos, 和物理驱动器. 在比较新的Linux发行版中, /media目录已经成为了I/O设备的首选挂载点.
/media
在比较新的Linux发行版中, I/O设备的首选挂载点, 比如CD ROM或USB flash驱动器.
/var
可变的(可修改的)系统文件. 这是一个包罗万象的"杂项"目录, 用于保存Linux/UNIX机器运行时产生的各种数据.
/var/log
系统范围的日志文件.
/var/spool/mail
用户的假脱机邮件(mail spool).
/lib
系统范围的库文件.
/usr/lib
更多系统范围的库文件.
/tmp
系统临时文件.
/boot
系统引导目录. 内核, 模块链接, 系统镜像, 和引导管理器都放在这.
Warning 如果在这个目录下修改文件, 可能会导致系统不能启动.
注意事项
[1] | 早期的UNIX系统一般都有两个磁盘设备, 一个是速度快但容量小的硬盘(主要包含/, 即根目录), 另一个磁盘容量大, 但是速度慢(主要包含/usr目录和其他分区). 所以, 使用频率最高的程序和工具都放到小而快的磁盘中, 也就是放到/bin中, 而其他的东西都放到慢磁盘上, 即/usr/bin中. 其他的类似的东西也是按照这种方式进行分类的, 比如/sbin和/usr/sbin, /lib和/usr/lib, 等等. |
将DOS批处理文件转换为Shell脚本
相当多的在PC上学习脚本的程序员都在运行DOS. 事实上, 残废的DOS批处理文件语言还是可以编写出一些比较强大的脚本来的, 虽然它们一般都需要借助于外部的工具. 所以说, 某些时候, 我们还是需要将老式的DOS批处理文件转换为UNIX shell脚本. 一般来说, 做这种事情并不困难, 因为DOS批处理文件操作不过是等价的shell脚本的一个受限子集.
批处理文件关键字 / 变量 / 操作符, 和等价的shell符号
批处理文件操作符 | Shell脚本等价符号 | 含义 |
---|---|---|
% | $ | 命令行参数前缀 |
/ | - | 命令选项标记 |
\ | / | 目录路径分隔符 |
== | = | (等于)字符串比较测试 |
!==! | != | (不等)字符串比较测试 |
| | | | 管道 |
@ | set +v | 不打印当前命令 |
* | * | 文件名"通配符" |
> | > | 文件重定向(覆盖) |
>> | >> | 文件重定向(附加) |
< | < | 重定向stdin |
%VAR% | $VAR | 环境变量 |
REM | # | 注释 |
NOT | ! | 取反 |
NUL | /dev/null | "黑洞"用来阻止命令输出 |
ECHO | echo | 打印(Bash中有更多选项) |
ECHO. | echo | 打印空行 |
ECHO OFF | set +v | 不打印后续的命令 |
FOR %%VAR IN (LIST) DO | for var in [list]; do | "for"循环 |
:LABEL | 没有等价物(多余) | 标签 |
GOTO | 没有等价物(使用函数) | 跳转到脚本的另一个位置 |
PAUSE | sleep | 暂停或等待一段时间 |
CHOICE | case or select | 菜单选择 |
IF | if | if条件语句 |
IF EXIST FILENAME | if [ -e filename ] | 测试文件是否存在 |
IF !%N==! | if [ -z "$N" ] | 参数"N"是否存在 |
CALL | source命令或.(点操作符) | "include"另一个脚本 |
COMMAND /C | source命令或.(点操作符) | "include"另一个脚本(与CALL相同) |
SET | export | 设置一个环境变量 |
SHIFT | shift | 左移命令行参数列表 |
SGN | -lt或-gt | (整形)符号 |
ERRORLEVEL | $? | 退出状态 |
CON | stdin | "控制台"(stdin) |
PRN | /dev/lp0 | (一般的)打印设备 |
LPT1 | /dev/lp0 | 第一个打印设备 |
COM1 | /dev/ttyS0 | 第一个串口 |
批处理文件一般都包含DOS命令. 我们必须把它转换为UNIX的等价命令, 这样我们才能把批处理文件转换为shell脚本文件.
DOS命令与UNIX的等价命令
DOS命令 | UNIX等价命令 | 效果 |
---|---|---|
ASSIGN | ln | 链接文件或目录 |
ATTRIB | chmod | 修改文件权限 |
CD | cd | 更换目录 |
CHDIR | cd | 更换目录 |
CLS | clear | 清屏 |
COMP | diff, comm, cmp | 文件比较 |
COPY | cp | 文件拷贝 |
Ctl-C | Ctl-C | 中断(信号) |
Ctl-Z | Ctl-D | EOF(文件结束) |
DEL | rm | 删除文件 |
DELTREE | rm -rf | 递归删除目录 |
DIR | ls -l | 列出目录内容 |
ERASE | rm | 删除文件 |
EXIT | exit | 退出当前进程 |
FC | comm, cmp | 文件比较 |
FIND | grep | 在文件中查找字符串 |
MD | mkdir | 新建目录 |
MKDIR | mkdir | 新建目录 |
MORE | more | 分页显示文本文件 |
MOVE | mv | 移动文件 |
PATH | $PATH | 可执行文件的路径 |
REN | mv | 重命名(移动) |
RENAME | mv | 重命名(移动) |
RD | rmdir | 删除目录 |
RMDIR | rmdir | 删除目录 |
SORT | sort | 排序文件 |
TIME | date | 显示系统时间 |
TYPE | cat | 将文件输出到stdout |
XCOPY | cp | (扩展的)文件拷贝 |
Note | 事实上, 几乎所有的UNIX和shell操作符, 还有命令都有许多的选项, 对比DOS和批处理文件来说, 它们要强大的多. 许多DOS批处理文件都需要依靠辅助工具, 比如ask.com, 这是一个比read命令差很多的类似副本. DOS对于文件名通配符扩展支持的非常有限, 并且很不完整, 仅仅识别*和?. |
将DOS批处理文件转换为sehll脚本, 通常是一件很简单的事情, 而且转换的结果通常都比原始的批处理文件好.
摘自:高级Bash脚本编程指南: 一本深入学习shell脚本艺术的书籍
参考:Shell学习宝典