1. 重定向输出
$ ls > lsoutput.txt
这条命令把 ls 命令的输出保存到文件 lsoutput.txt 中。默认情况下,如果该文件已经存在,它的内容将被覆盖。如果想改变默认行为,可以用 set -C 命令设置noclobber 选项。
文件描述符 0 代表一个程序的标准输入,1 代表标准输出,2 代表标准错误输出。
$ ps >> lsoutput.txt
这条命令会将 ps 命令的输出附加到指定文件的尾部。
$ lill -HUP 1234 >killout.txt 2>killerr.txt
上面的命令会把标准输出和标准错误输出分别重定向到不同的文件中。
2. 重定向输入
$ more < killout.txt
一个很简单的重定向输入的例子。
3. 管道:管道操作符 | 来连接进程。在 linux 下通过管道连接的进程可以同时运行,并且随着数据流在它们之间的传递可以自动地进行协调。如:
$ ps > psout.txt $ sort psout.txt > pssort.txt
更精巧的一个解决方案是用管道来连接进程:
$ ps | sort > pssort.out
允许连接的进程书目是没有限制的。假设我们想看看所有系统中运行的进程的名字,但不包括 shell 本身,可以使用下面的命令:
$ ps -xo comm | sort | uniq | grep -v sh | more
这个命令首先按照字母顺序排序 ps 命令的输出,再用 uniq 命令去除重复的内容,然后用 grep -v sh 命令删除名为 sh 的进程,最终分页显示在屏幕上。
注意:如果有一系列的命令需要执行,相应的输出文件是在这一组命令被创建的同时立刻被创建或写入的,所以决不要再命令流中重复使用相同的文件名。如:
$ cat mydata.txt | sort | uniq | > mydata.txt
你最终将会得到一个空文件,因为你再读取文件 mydata.txt 之前就已经覆盖了这个文件的内容。
4. shell 也提供了通配符扩展
*:匹配任意一个字符串;
? : 匹配单个字符;
[ set ] : 允许匹配方括号中任何一个单个字符;
[ ^set ] : 匹配任何没有出现在给出的字符集中的字符;
{ } : 允许将任意的字符串将任意的字符串分组放在一个集合中,以供 shell 进行扩展。
5. 创建脚本
#!/bin/bash # first # This file looks through all the files in the current # directory for the string iostream, and then prints the names of # those files to the standard output. for file in * do if grep -q iostream $file then echo $file fi done exit 0
程序中的注释以 # 符号开始,一直持续到该行的结束。按照惯例,我们通常把#放在第一行。明确了这一点后,请注意第一行#!/bin/bash ,它是注释语句的一种特殊形式,#!字符告诉系统同一行上紧跟在它后面的那个参数是用来执行文本文件的程序。
exit 命令的作用是确保脚本程序能够返回一个有意义的退出码。当程序以交互方式运行时,我们很少需要检查它的退出码,但如果打算从另一个脚本程序里调用这个脚本程序并查看它是否执行成功,则返回一个适当的退出码就很重要了。即使你从来没有想过允许你的脚本程序被另一个脚本程序调用,你也应该在退出时返回一个合理的退出码。请相信自己的脚本程序是有用的,它总有一天会作为其他脚本程序的一部分而被重用。
6. 检查文件是否是脚本程序的最好方法是使用 file 命令,例如 file first 或 file /bin/bash。
7. 可以通过 $ /bin/sh first 执行刚刚的文件,也可以 chmod 给 first 文件可执行的权限,然后 ./first 执行它。
8. 在确信脚本程序能够正确执行后,则可以把它从当先目录移到一个更合适的地方去。如果这个命令只供本人使用,可以在自己的主目录中创建一个 bin 目录,当且将它添加到自己的 PATH 变量中。如果想让其他人也能够执行这个脚本程序,可以将/usr/local/bin 或者其他系统目录作为添加新程序的位置。
9. 为了防止其他用户修改脚本程序,应该去掉脚本程序的写权限。
10. 变量
1)使用变量前通常不需要事先为它们做出声明。只是简单地通过使用它们(比如当我们给它们赋初始值时)来创建它们。
2)默认情况下所有变量都被看作字符串并以字符串来存储,即使它们被赋值为数值时也是如此。
3)shell 和一些工具程序会在需要时把数值型字符串转换为对应的数值以对它们进行操作。
4)linux 是一个大小写敏感的系统。
5)可以通过在变量名前加一个 $ 符号来访问它的内容。
6)当为变量赋值时,我们只需要使用变量名,如果需要,该变量就会被自动创建。
7)检查变量内容的简单方式就是在变量名前加一个 $ 符号,再用 echo 命令将它的内容输出到终端上。如:
$ salutation=Hello $ echo $salutation Hello $ salutation="Yes Dear" $ echo $salutation Yes Dear $ salutation=7+5 $ echo $salutation 7+5
注意:如果字符串里包含空格,就必须用引号把它们括起来,还要注意在等号两边不能有空格。
8)可以通过使用 read 命令来将用户的输入赋值给一个变量。这个命令需要一个参数,即准备读入用户输入的数据的变量名,然后它会等待用户输入数据。通常情况下,在用户按下回车键时,read 命令结束。当从终端读取一个变量时,我们一般不需要使用引号。
9)如果想在一个参数中包含一个或多个空白字符,就必须给参数加上引号。
10)如果把一个带有 $ 字符的变量放在双引号中,程序执行到这一行时就会把变量替换为它的值;如果把它放在单引号中,就不会发生替换现象。还可以通过 \ 字符取消特殊含义。
#!/bin/sh myvar="Hi there" echo $myvar echo "$myvar" echo '$myvar' echo \$myvar echo Enter some text read myvar echo '$myvar' now equals $myvar exit 0
输出结果如下:
Hi there Hi there $myvar $myvar Enter some text abc $myvar now equals abc
11)环境变量:一些变量会根据环境设置中的值进行初始化。这些变量通常用大写字母做名字,以便把它们和用户在脚本程序中定义的变量区分开来,后者按惯例都用小写字母做名字。一些比较重要的环境变量:
$HOME 当前用户的主目录
$PATH 以冒号分隔的用来搜索命令的目录列表
$PS1 命令提示符,通常是 $ 字符,但在 bash 中,你可以使用一些更复杂的值。例如,字符串 [\u@\h\W] $ 就是一个流行的默认值,它给出用户名、 机器名和当前目录名,还有一个 $ 提示符
$PS2 二级提示符,用来提示后续的输入,通常是 > 字符
$IFS 输入域分隔符。当 shell 读取输入时,用来分隔单词的一组字符,它们通常是空格、制表符和换行符
$0 shell 脚本的名字
$# 传递给脚本的参数个数
$$ shell 脚本的进程号,脚本程序通常会用它来生成一个唯一的临时文件,如 /tmp/tmpfile_$$
12)参数变量:脚本程序在调用时带有参数,就会创建一些额外的变量。即使没有传递任何参数,环境变量 $# 也依然存在,值为0 。脚本的参数为 $1, $2, ... 。$* 在一个变量中列出所有的参数,各个参数之间用环境变量 IFS 的第一个字符分隔开。
11. 条件: test 或 [ 命令,都是布尔判断命名,当使用 [ 命令时,还要用 ] 来结尾。如
if test -f fred.c then ... fi
我们还可以写成这样:
if [ -f fred.c ] then ... fi
test 命令的退出码(表明条件是否被满足)决定是否需要执行后面的条件语句。
注意:必须在 [ 符号和被检查的条件之间留出一个空格。
如果喜欢 then 和 if 放在同一行上,就必须要用一个分号把 test 语句和 then 分开。
12. test 命令可以使用的条件类型可以分为三类:字符串比较、算数比较和文件有关的条件测试。
字符串比较
string1 = string2 如果两个字符串相同,则结果为真
string1 != string2 如果两个字符串不同,则结果为真
-n string 如果字符串不为空则结果为真
-z string 如果字符串为空,则结果为真
算术比较
expression1 -eq expression2 如果两个表达式相等则结果为真
expression1 -ne expression2 如果两个表达式不等则结果为真
expression1 -gt expression2 如果 expression1 大于 expression2 则结果为真
expression1 -ge expression2 如果 expression1 大于等于 expression2 则结果为真
expression1 -lt expression2 如果 expression1 小于 expression2 则结果为真
expression1 -le expression2 如果 expression1 大于等于 expression2 则结果为真
! expression 如果表达式为假,则结果为真,反之亦然
文件条件测试
-d file 如果文件是一个目录则结果为真
-e file 如果文件存在则结果为真
-f file 如果文件是一个普通文件则结果为真
-g file 如果文件的 SGID 位被设置则结果为真
-r file 如果文件可读则结果为真
-s file 如果文件的长度不为 0 则结果为真
-u file 如果文件的 SUID 位被设置则结果为真
-w file 如果文件可写则结果为真
-x file 如果文件可执行则结果为真
13. if 语句结构
if condition then statements else statements fi
14. elif 语句
if condition then statements elif statements else statements fi
15. for 语句
for varialbe in values do statements done
16. while 语句
while condition do statements done
17. untile 语句
until condition do statements done
它与 while 循环很相似,只是把条件测试反过来了。循环将反复执行知道条件为真,而不是在条件为真时反复执行。
18. case 语句
case varible in pattern [ | pattern ] ...) statements;; pattern [ | pattern ] ...) statements;; ... esac
看上去有些强制,但 case 结构允许我们通过一种比较复杂的方式将变量的内容和模式进行匹配,然后再根据匹配的模式去执行不同的代码。每个模式行都以双分号结尾。
样例:
#!/bin/bash echo "Is it moring? Please answer yes or no" read timeofday case "$timeofday" in yes) echo "Good Morning";; no) echo "Good Afternoon";; y) echo "Good Morning";; n) echo "Good Afternoon";; *) echo "Sorry, answer not recognized";; esac exit 0
样例2:
#!/bin/bash echo "Is it moring? Please answer yes or no" read timeofday case "$timeofday" in yes | y | Yes | YES) echo "Good Morning";; n* | N*) echo "Good Afternoon";; *) echo "Sorry, answer not recognized";; esac exit 0
注意:*通配符的扩展在单引号中不起作用
19. AND 列表
statements1 && statements2 && statements3 && statements4 && ...
从左开始顺序执行每条指令,如果一条命令返回的是true,它右边的下一条命令才能够执行。如此循环直到有一条命令返回 false,或者列表中的所有命令都执行完毕。
作为整体,如果 AND 列表中的所有命令都执行成功,就算执行成功,否则就算它失败。
20. OR 列表
statements1 || statements2 || statements3 || statements4 || ...
从左开始顺序执行每条指令,如果一条命令返回的是 false,它右边的下一条命令才能够执行。如此循环直到有一条命令返回 true,或者列表中的所有命令都执行完毕。
作为整体,如果 OR 列表中的所有命令都执行失败,就算执行失败,否则就算它成功。
21. 语句块:用 { } 将多个语句可以看成一条语句
22. 函数
定义函数
function_name(){ statements }
例子
#!/bin/bash foo(){ echo "Function foo is executing" } echo "script starting" foo echo "script ended" exit 0
该程序从顶部开始执行,当它遇到 foo() { 结构时,它知道定义了一个名为 foo 的函数。它会记住 foo 代表这一个函数并从 } 字符之后的位置开始执行。当执行到 foo 时,shell 就知道应该去执行刚才定义的函数了,当这个函数执行完毕后,执行过程会返回到调用 foo 函数的那条语句的后面继续执行。
在调用一个函数之前,必须对它进行定义。
当一个函数被调用时,脚本程序的位置参数 $* 、$@、$#、$1、$2 等会被替换为函数的参数。这也是读取传递给函数的参数的办法。当函数执行完毕后,这些参数就会恢复为它们先前的值。
可以通过 return 命令让函数返回数字值。
让函数返回字符串值的常用方法是让函数将字符串保留在一个变量中,而该变量应该可以在函数结束后被使用。
如果函数里没有使用 return 命令指定一个返回值,函数返回的就是执行的最后一条命令的退出码。
使用 local 关键字在 shell 函数中声明局部变量,局部变量将局限在函数的作用范围内。如果一个局部变量和一个全局变量的名字相同,前者就会覆盖后者,但仅限于函数的作用范围之内。
23. break 命令: 跳出 for 、while 或 until 循环,默认情况下,break 只跳出一层循环。
24. : 命令
它偶尔会被用于简化条件的逻辑,相当于 true 的一个别名。
25. continue 命令: 使 for 、 while 或 until 循环跳到下一次循环继续执行,循环变量取循环列表中的下一个值。
26. . 命令:点(.)命令用来执行当前 shell 中的命令。
27. eval 命令: 它允许你对参数进行求值。
例子
foo=10 x=foo y='$'$x echo $y
它输出“$foo”,而
foo=10 x=foo eval y='$'$x echo $y
它输出10。因此,eval 命令有点像一个额外的 $,它给出一个变量的值的值。
28. exec 命令
1)将当前 shell 替换为一个不同的程序。例如:exec wall "Thanks for all the fish",脚本中的这个命令会用 wall 命令替换当前的 shell 。脚本程序中 exec 命令后面的代码都不会执行,因为执行这个脚本的 shell 已经不存在了。
2)修改当前的文件描述符。例如:exec 3< afile 这使得文件描述符3被打开以便从文件 afile 中读取数据。这种用法非常少见。
29. export 命令: 将作为它参数的变量导出到子 shell 中,并使之在子 shell 中有效。
30. expr 命令:将它的参数当作一个表达式来求值。例如:x=`expr $x + 1`。
expr1 | expr2 如果 expr1 非零,则等于 expr1 ,否则等于 expr2
expr1 & expr2 只要有一个表达式为零,则等于零,否则等于 expr1
expr1 = expr2
expr1 > expr2
expr1 >= expr2
expr1 <= expr2
expr1 != expr2
expr1 + expr2
expr1 - expr2
expr1 * expr2
expr1 / expr2
expr1 % expr2
在最新的脚本程序中,expr 命令通常被替换为更有效的 $(( . . . ))语法。
31. printf 命令: 语法为 printf “format string” parameter1 parameter2 . . .
和 c/c++ 中使用的非常相似,但有一些自己的限制。主要是不支持浮点数,因为 shell 中所有的算术运算都是按照整数来进行计算的。
32. set 命令:为 shell 设置参数变量。
33. shift 命令:把所有参数变量左移一个位置,是 $2 变成 $1,$3 变成 $2 ,以此类推。原来的 $1 的值将被丢弃 $0仍将保持不变。
34. trap 命令:用于指定在接收到信号后将要采取的行动。
trap 命令的一种常见用途是在脚本程序被中断时完成清理动作。
trap command signal
参数的前一步分是接收到指定信号时将要采取的行动,后一部分是要处理的信号名。
35. unset 命令:从环境中删除变量或函数。不能删除 shell 本身定义的只读的变量(如 IFS)。不常用。
36. find 命令:查找文件
find [path] [options] [tests] [actions]
path:很容易理解,绝对路径相对路径都行
options:列出一些主要的选项
-depth 在查看目录本身之前先搜索目录的内容
-follow 跟随符号链接
-maxdepths N 最多搜索 N 层目录
-mount( 或 -xdev) 不搜索其他文件系统中的目录
test:列出一些最常用的测试
-atime N 文件在 N 天之前被访问过
-mtime N 文件在 N 天之前被修改过
-name pattern 文件名(不包括路径名)匹配提供的模式 pattern,为了确保 pattern 被直接传递给 find 命令而不是由 shell 来处理,pattern 必须总是用 引号括起
-newer otherfile 文件比 otherfile 要新
-type c 文件的类型为 C,C可以是一个特殊类型。最普通的类型是 d (目录) 和 f(普通文件)。其他可用类型请参考使用手册
-user username 文件的拥有者是指定的用户 username
actions:列出一些最常见的动作
-exec command 执行一条命令。
-ok command 与 -exec 类似,但它在执行命令之前会针对每个要处理的文件,提示用户进行确认
-print 打印文件名
-ls 对当前的文件使用命令 ls -dils
37. grep 命令:通用正则表达式解析器
find 命令在系统中搜索文件,而使用 grep 命令在文件中搜索字符串。
grep [options] PATTERN [FILES]
options:列出主要选项
-c 输出匹配行的数目,而不是输出匹配的行
-E 启用扩展表达式
-h 压缩每个输出行的普通前缀为匹配查询模式的文件名
-i 忽略大小写
-l 只列出包含匹配行的文件名,而不输出真正的匹配行
-v 对匹配模式取反,即搜索不匹配行而不是匹配行
38. here 文档:在 shell 脚本程序中向一条命令传递输入的一种特殊方法是使用 here 文档。它允许一条命令在执行时就好像是在读取一个文件或键盘一样,而实际上是从脚本程序得到输入数据。
以两个连续的小于号 << 开始,紧跟着一个特殊的字符序列,该序列将在文档的结尾处再次出现。
声明:
本文为 iddmx 对《Beginning Linux Programming》(Third Edition) Chapter 2 的读书笔记。内容大部分都是书中内容,也有一些个人理解,由于 iddmx 水平有限,书中许多精华没能提取出来,如果想要深入了解,请阅读原书。
本文欢迎自由转载,但请务必保持本文完整或注明来之本文。本文未经 iddmx 同意,不得用于商业用途。最后,如果您能从这个简单文档里获得些许帮助,iddmx 将对自己的一点努力感到非常高兴;iddmx 水平有限,如果本文中包含的错误给您造成了不便,iddmx 在此提前说声抱歉,并希望您能j将错误告知 iddmx([email protected]),以便修正。
祝身体健康,工作顺利。☺