<<Shell 脚本学习指南>>(Classic Shell Scripting) Shell - 命令解析器 // ------------------------------------------ 1, 位于第一行的 #!, 告知 *nix 以哪个 shell 来执行所指定的 shell 脚本,可以加上选项。 #! /bin/sh - // 选项 - 表示没有 shell 选项 2, shell 的基本元素 命令与参数 命令名称是命令行的第一个项目,后面通常会跟着选项,任何额外的参数都会放在选项之后;选项开头是一个 -,后面跟着一个字母 两个 -(--)表示选项的结尾 // 变量 var_name=helloworld # 中间不能有任何的空格 var_name_2="hello world" # 因为 UNIX 以空白(space 或 tab)隔开命令行中各个组成部分 3, 特殊文件: /dev/null /dev/tty /dev/null 传送到此文件的数据都会被系统丢掉;读取该文件时会立即返回文件结束符号(end-of-file) /dev/tty 当程序打开此文件时,UNIX 会自动将它重定向到一个终端 4, 访问 Shell 脚本参数 位置参数: $1, $2, ..., $9, ${10}, ${11}, ... 5, 执行追踪 sh -x ./test.sh 也可以在 shell 脚本使用 set -x 命令打开执行跟踪; set +x 关闭 ------------------------------------------ 1, 大部分简易程序都是处理输入数据的行,像 grep 与 egrep 及 sed 大部分的工作(90%) 2, 在文本文件中,一行表示一条记录。 3, 字段分割: i. 使用空白(space 或 tab) ii. 使用特定的定界符 // 使用空白分割时,通常多个连续出现的空格或制表字符都将看到作一个定界符; // 若使用特殊字符分割,则每个定界符都隔开一个字段 1, export 将变量放进环境里。环境是一个名称与值的简单列表,可空所有执行中的程序使用。新的进程会从其父进程继承环境,也可以在建立新的进程之前修改它 export -p 可用于显示当前的环境 2, env 可临时的改变环境变量值 env -i PATH=$PATH HOME=$HOME awk '...' file1 file2 -i 使用来初始化环境变量的; 3, unset 可从执行中的 shell 中删除变量与函数,默认删除变量 unset -v first middle last unset -f my_func 4, 参数展开 ${varname:-word} varname 存在且非 null,返回其值;否则返回 word ${varname:=word} varname 存在且非 null,返回其值;否则设置它为 word,并返回 word ${varname:?message} varname 存在且非 null,返回其值;否则显示 message ${varname:+word} varname 存在且非 null,返回 word;否则返回 null # 以上所有都可去掉 : ,则 "存在且非 null" 改为 "存在" 5, 模式匹配 ${variable#pattern} ${variable##pattern} ${variable%pattern} ${variable%%pattern} 6, 位置参数 $# $* $@ "$*" "$@" 7, 特殊变量 $# $@ $* $- $? $$ $0 $! $ENV $HOME $IFS $PATH $PPID $PS1 $PS2 $PWD 8, 算术展开 $((4>3)) 值为 1 count=4 $((count++)) # $count 为 5 9, test 命令 test 命令的另一种形式是 [ ... ] ---- if test -f ./file then echo ./file does exist else echo ./file does not exist fi -- [ ... ] 形式 if [ -f ./file ] # 注意: [ ... ] 方括号必须与其中的表达式一空格隔开 then echo ./file does exist else echo ./file does not exist fi ---- 10, break 和 continue 可以接受可选的数值参数 11, shift 处理命令行参数,一次向左位移一位;可以接受一个可选的参数,表示移动的位数 许多 Unix 程序都遵循 标准输入/输出(Standard I/O) 的设计原则,它的构想是:程序应有一个数据来源,数据出口,以及报告错误的地方;分别叫做标准输入(Standard input),标准输出(Standard output)和标准错误输出(Standard error)。程序默认情况下会读取标准输入,写入标准输出,并将错误信息传递给标准错误。默认的标准输入,标准输出及标准错误输出都是终端,当登录时,Unix 便将默认的标准输入,标准输出,标准错误安排为终端。 一般情况下,当程序运行时,它会自动打开这三个文件。I/O 从定向就是通过与终端交互,或是在 shell 脚本里设置,重新安排从哪里输入或输出到哪里。 // shell 是命令解析器,命令是 shell 的基本元素。shell 在执行 shell 脚本的命令时要识别每个单独的命令然后在 PATH 里寻找该命令执行,因此一个命令一般是占一行;一些符号可以用来分割命令,像 ; | & && || 1, read 读取行。 read -r variable ... # 将信息读入一个或多个 shell 变量 2, set -C # 禁止从定向覆盖 3, <> 打开一个文件作为输入与输出只用 # 在很多环境下,他的运行会有问题 4, 文件描述符- Unix 以一个小的整数数字表示每个进程的打开文件 文件描述符,0, 1, 2 各自对应到标准输入,标准输出及标准错误输出 make 1>results 2>ERRORS 将 make 的标准输出传给 results 文件,标准错误输出写入 ERRORS 文件 # make 程序运行时,> 会从定向打开的标准文件,若 make 向标准文件写入信息,就会写入从定向的文件内。 5, ~ 展开 ~ 表示当前登录用户的根目录 ~username 表示 username 的根目录 ~+ 和 $PWD 变量值相同 6, shell 基本通配符 ? 任何单一字符 * 任何的字符字符串 [set] 任何在 set 里的字符 [!set] 任何不再 set 里的字符 -- [a-c] a b c [a-zA-Z0-9_-] 任何一个字母,任何一个数字,下划线或中划线 7, 命令替换 - shell 执行命令,并替换为命令执行后的结果 `` # 反引号 name=`echo hello` $() # name=$(echo hello) 8, 数学运算 expr expr 被设置在命令替换以内 i=0 i=`expr $i + 1` # 此时 $i 为 1 9, 引用 i. 反斜杠转义 ii. 单引号 会强制 shell 将一对引号之间的所有字符都看作其字面上的意义。不可以在一个单引号引用的字符串里在捏前一个但引号,即便是反斜杠,在但引号里也没有特殊意义。 iii. 双引号 双引号会确切地处理括起来的文字中的转移字符和变量,算术,命令替换 在双引号里,字符 $ " ` / 如用到字面上的意义,必须前置 /。 其他任何的字符前面的反斜杠都不带有任何特殊意义。// remember 10, (...) subShell 是一群被括在圆括号里的命令,这些命令在另外的进程中执行。当需要让一小组的命令在不同的目录下执行时,这种方法可以让你不必修改主脚本的目录。 tar -cf - . | (cd /newdir; tar -xpf -) 11, {...} 代码块(code block), 基本与 subShell 相同,不过它不会建立新进程,代码块里的命令以花括号括起来,且对主脚本的状态会造成影响 12, shell 执行命令时的查找顺序是: 特殊的内建命令 > shell 函数 > 一般内建命令 command program # 可以避开 shell 的包含函数 13, set 命令 i. 不加参数,会以排序的方式显示所有 shell 变量的名称与值 ii. set -- # 改变位置参数 iii. 打开或停用 shell 选项 /** * awk */ awk 读取每个文件,用 RS 将每个文件分为记录,然后用 FS 将每个记录切分为字段。这个过程是自动完成的。 awk 中除 BEGIN 与 END 的模式都是对每条记录匹配,如果匹配成功则执行模式对应的操作。 // 1, man awk # 查看手册中详细介绍 2, awk 有标量和数组两种类型 3, 字符串一双引号引起,不能用 单引号 没有字符串连接运算符,两个字符串在一起自然的被连接起来 "hello" "world" # 等于 "helloworld" 4, 数组 awk 的数组和 PHP 数组几乎一样 i. 任何数字和字符串可作为索引 ii. 数组元素类型可以不一样 不同的是: iii. 数组可以是多索引数组 name["myhere", "zhang"] 5, 命令行参数 ARGC 和 ARGV # 和 PHP 的 cli 意义一样 内建数组 ENVIRON,保存所有环境变量 6, 变量无须被声明,在第一次使用的时候自动被建立 一个变量不能同时用作标量变量和数组变量,如: names[1] = "myhere" # 这是错误的!!! delete names[1] names = "zhang" 7, 数组成员的测试 key in array # 成员测试, split("", array) # 清空数组 8, getline getline # 读取文件下一条记录到 $0,并更新 NF, NR, FNR getline var # 读取下一条记录到 var, 更新 NR, FNR getline < file # 从 file 读下一条记录到 $0, 更新 NF getline var < file # 从 file 读下一条记录到 var cmd | getline # 从外部命令读取下一条记录到 $0, 更新 NF cmd | getline var # 从外部命令读取下一条记录,存入 var 9, close 关闭文件 在 awk 中,文件打开后便会一直保持在打开状态,直到明确指出要关闭他或知道程序终结 打开的文件在没有 close 的情况下不能混用 > 和 >> (打开文件的方式不同) 10, system(command) 执行外部程序, system() 执行外部程序不需要用 close() 关闭文件 symtem("date") # close() 仅针对以 I/O 重定向运算符所打开的文件或管道,还有 getline,print,printf 11, 用户自定义的函数 function sayHello(name){ print "hello " name; } 和 shell 一样,awk 中全局变量在自定义函数中自动可见( C,JavaScript 也是这样,PHP 不是); 函数定义参数列表中的参数都是局部参数,在函数体中的且未出现在函数定义参数列表中的参数都被认为是全局变量; awk 允许函数调用是传送的参数比定义时少,因此可以将函数中用到的变量放到函数定义中,防止污染全局变量环境 function find_key(array, value, key) # key 前置几个空格,以区分 { for( key in array){ if( array[key] == value){ return key } } return "" } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Shell 各版本规模 V7 Bourne Shell < POSIX Shell < (ksh93, bash) // 共通的 Shell 扩展 1, select 循环 select name [in list] do # do something done 2, [[ ... ]] [[ ... ]] 于 [ ... ] 不同之处在于不处理单词展开与样式展开(通配符)。意即它不需要使用引号以处理引文操作 3, 索引式数组 name=(val1 val2 val3) ${#name[*]} name 数组长度 # ${#str} 字符串长度