Add a bunch of changes

1. Update .gitignore with gitbook official one

2. Add gitbook building support in Makefile

3. Add a tock build script: tools/build-toc.sh

4. Insert the table of content for every page, it is very useful for such long articles.

Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
This commit is contained in:
Wu Zhangjin
2015-07-09 21:51:26 +08:00
parent 4aed6a0da1
commit 00f29bdffd
15 changed files with 718 additions and 150 deletions
+33 -15
View File
@@ -1,15 +1,33 @@
kaiyuanbook.*
*.pdf
*.swp
*.aux
*.log
*.html
latex/*
!latex/makepdf
!latex/README
!latex/template.tex
!latex/config.yml
epub/temp
*~
.#*
*.epub
kaiyuanbook.*
*.pdf
*.swp
*.aux
*.log
*.html
latex/*
!latex/makepdf
!latex/README
!latex/template.tex
!latex/config.yml
epub/temp
*~
.#*
*.epub
# Node rules:
## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
## Dependency directory
## Commenting this out is preferred by some people, see
## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
node_modules
# Book build output
_book
# eBook build output
*.epub
*.mobi
*.pdf
*.html
+2
View File
@@ -22,9 +22,11 @@ bookOutput := $(outDir)/$(long_fileName)
# build targets
all: $(bookInput)
gitbook build
$(bookInput): clean
@$(make_pdf)
gitbook pdf
read:
evince $(bookInput)
+4
View File
@@ -1,3 +1,7 @@
> 本书-来源:[开源书籍:Shell 编程范例](http://www.tinylab.org/project/pleac-shell/) (by [泰晓科技](http://tinylab.org))<br>
> 报名/参与:*Star/fork* [open-shell-book github 仓库](https://github.com/tinyclub/open-shell-book); *关注/私信* [@泰晓科技](http://weibo.com/tinylaborg) <br>
# Shell 编程范例
v0.1
+32
View File
@@ -0,0 +1,32 @@
#!/bin/bash
#
# Build toc for markdown automatically
#
for i in *.markdown
do
# Generate a random toc id to avoid conflict
toc="toc_${RANDOM}_${RANDOM}_"
# Generate table of content
sed -i -e "2i\n" $i
grep "^###* " -ur $i | grep -n "^#" | \
sed -e "s/:/a/g;" |\
sed -e "s/\([0-9]*\)a\(#[^ ]*\) \(.*\)/\1a\2 [\3](#$toc\1)/g" |\
sed -e "s/#####/+ - /g;s/####/+ - /g" |\
sed -e "s/###/+ - /g;s/##/- /g" |\
xargs -i sed -i -e "{}" $i;
sed -i -e "2i\n" $i
sed -i -e "s/^+ / /g;s/^n$//g" $i
# Replace the #* with h* + id info
t=0
for line in `grep -n "^##" $i | cut -d':' -f1`
do
((line+=t))
((t++))
sed -i -e "${line}i<span id=\"$toc$t\"></span>" $i
done
done
+87 -19
View File
@@ -1,16 +1,55 @@
# 附录
- [Shell编程学习笔记](#toc_19246_27800_1)
- [前言](#toc_19246_27800_2)
- [执行Shell脚本的方式](#toc_19246_27800_3)
- [范例:输入重定向到Bash](#toc_19246_27800_4)
- [范例:以脚本名作为参数](#toc_19246_27800_5)
- [范例:以 . 来执行](#toc_19246_27800_6)
- [范例:直接执行](#toc_19246_27800_7)
- [Shell的执行原理](#toc_19246_27800_8)
- [变量赋值](#toc_19246_27800_9)
- [范例:获取当前的工作目录并存放到变量中](#toc_19246_27800_10)
- [数组](#toc_19246_27800_11)
- [范例:对数组元素赋值](#toc_19246_27800_12)
- [范例:访问某个数组元素](#toc_19246_27800_13)
- [范例:数组组合赋值](#toc_19246_27800_14)
- [范例:列出数组中所有内容](#toc_19246_27800_15)
- [范例:获取数组元素个数](#toc_19246_27800_16)
- [参数传递](#toc_19246_27800_17)
- [设置环境变量](#toc_19246_27800_18)
- [键盘读起变量值](#toc_19246_27800_19)
- [设置变量的只读属性](#toc_19246_27800_20)
- [条件测试命令test](#toc_19246_27800_21)
- [范例:数值比较](#toc_19246_27800_22)
- [范例:测试文件属性](#toc_19246_27800_23)
- [范例:字符传属性以及比较](#toc_19246_27800_24)
- [范例:串比较](#toc_19246_27800_25)
- [整数算术或关系运算expr](#toc_19246_27800_26)
- [控制执行流程命令](#toc_19246_27800_27)
- [范例:条件分支命令if](#toc_19246_27800_28)
- [范例:case命令举例](#toc_19246_27800_29)
- [范例:循环语句while, until](#toc_19246_27800_30)
- [范例:有限循环命令for](#toc_19246_27800_31)
- [函数](#toc_19246_27800_32)
- [后记](#toc_19246_27800_33)
<span id="toc_19246_27800_1"></span>
## Shell编程学习笔记
<span id="toc_19246_27800_2"></span>
### 前言
这是作者早期的Shell编程学习笔记,主要包括Shell概述、Shell变量、位置参数、特殊符号、别名、各种控制语句、函数等Shell编程知识。
要想系统地学Shell,应该找些较系统的资料,例如:[《Shell编程范例序列》](http://www.tinylab.org/shell-programming-paradigm-series-index-review/)和[《鸟哥学习Shell Scripts》](http://www.chinaunix.net/jh/24/628472.html)。
<span id="toc_19246_27800_3"></span>
### 执行Shell脚本的方式
#### 范例:输入重定向到Bash ####
<span id="toc_19246_27800_4"></span>
#### 范例:输入重定向到Bash
```
$ bash < ex1
@@ -18,7 +57,8 @@ $ bash < ex1
可以读入ex1中的程序,并执行
#### 范例:以脚本名作为参数 ####
<span id="toc_19246_27800_5"></span>
#### 范例:以脚本名作为参数
其一般形式是:
@@ -34,7 +74,8 @@ $ bash ex2 /usr/meng /usr/zhang
其执行过程与上一种方式一样,但这种方式的好处是能在脚本名后面带有参数,从而将参数值传递给程序中的命令,使一个Shell脚本可以处理多种情况,就如同函数调用时可根据具体问题传递相应的实参。
#### 范例:以 . 来执行 ####
<span id="toc_19246_27800_6"></span>
#### 范例:以 . 来执行
如果以当前Shell(以·表示)执行一个Shell脚本,则可以使用如下简便形式:
@@ -43,7 +84,8 @@ $ bash ex2 /usr/meng /usr/zhang
$ · ex3[参数]
```
#### 范例:直接执行 ####
<span id="toc_19246_27800_7"></span>
#### 范例:直接执行
将Shell脚本的权限设置为可执行,然后在提示符下直接执行它。
@@ -60,6 +102,7 @@ $ ./ex4
#!/bin/bash
```
<span id="toc_19246_27800_8"></span>
### Shell的执行原理
@@ -67,12 +110,14 @@ Shell接收用户输入的命令(脚本名),并进行分析。如果文件
因此,用户开发的Shell脚本可以驻留在命令搜索路径的目录之下(通常是“/bin”、 “/usr/bin”等),像普通命令一样使用。这样,也就开发出自己的新命令。如果打算反复使用编好的Shell脚本,那么采用这种方式就比较方便。
<span id="toc_19246_27800_9"></span>
### 变量赋值
可以将一个命令的执行结果赋值给变量。有两种形式的命令替换:一种是使用倒引号引用命令,其一般形式是: `命令表`
#### 范例:获取当前的工作目录并存放到变量中 ####
<span id="toc_19246_27800_10"></span>
#### 范例:获取当前的工作目录并存放到变量中
例如:将当前工作目录的全路径名存放到变量dir中,输入以下命令行:
@@ -86,12 +131,14 @@ $ dir=`pwd`
$ dir=$(pwd)
```
<span id="toc_19246_27800_11"></span>
### 数组
Bash只提供一维数组,并且没有限定数组的大小。类似与C语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标。下标可以是整数或算术表达式,其值应大于或等于0。用户可以使用赋值语句对数组变量赋值。
#### 范例:对数组元素赋值 ####
<span id="toc_19246_27800_12"></span>
#### 范例:对数组元素赋值
对数组元素赋值的一般形式是:`数组名[下标]=值`,例如:
@@ -108,7 +155,8 @@ $ city[2]=Tianjin
$ declare -a 数组名
```
#### 范例:访问某个数组元素 ####
<span id="toc_19246_27800_13"></span>
#### 范例:访问某个数组元素
读取数组元素值的一般格式是: `${数组名[下标]}` ,例如:
@@ -118,7 +166,8 @@ $ echo ${city[0]}
Beijing
```
#### 范例:数组组合赋值 ####
<span id="toc_19246_27800_14"></span>
#### 范例:数组组合赋值
一个数组的各个元素可以利用上述方式一个元素一个元素地赋值,也可以组合赋值。定义一个数组并为其赋初值的一般形式是:
@@ -140,7 +189,8 @@ $ echo ${A[8]}
若没有给出数组元素的下标,则数组名表示下标为0的数组元素,如city就等价于`city[0]`
#### 范例:列出数组中所有内容 ####
<span id="toc_19246_27800_15"></span>
#### 范例:列出数组中所有内容
使用`*``@`做下标,则会以数组中所有元素取代。
@@ -150,7 +200,8 @@ $ echo ${A[*]}
this is an example of shell script
```
#### 范例:获取数组元素个数 ####
<span id="toc_19246_27800_16"></span>
#### 范例:获取数组元素个数
```
@@ -159,6 +210,7 @@ $ echo ${#A[*]}
```
<span id="toc_19246_27800_17"></span>
### 参数传递
@@ -196,6 +248,7 @@ $ X=5 Y=10 ./add
可以发现,同样可以得到正确结果。
<span id="toc_19246_27800_18"></span>
### 设置环境变量
@@ -207,6 +260,7 @@ $ export opid=True
这样子就可以,如果要登陆后都生效,可以直接添加到`/etc/profile`或者`~/.bashrc`里头。
<span id="toc_19246_27800_19"></span>
### 键盘读起变量值
@@ -219,6 +273,7 @@ $ read -p "请输入一个值 " input ; echo "你输入了一个值为 "
```
<span id="toc_19246_27800_20"></span>
### 设置变量的只读属性
@@ -229,13 +284,15 @@ $ oracle_home=/usr/oracle7/bin
$ readonly oracle_home
```
<span id="toc_19246_27800_21"></span>
### 条件测试命令test
语法:`test 表达式`
如果表达式为真,则返回真,否则,返回假。
#### 范例:数值比较 ####
<span id="toc_19246_27800_22"></span>
#### 范例:数值比较
先给出数值比较时常见的比较符:
@@ -246,7 +303,8 @@ $ readonly oracle_home
$ test var1 -gt var2
```
#### 范例:测试文件属性 ####
<span id="toc_19246_27800_23"></span>
#### 范例:测试文件属性
文件的可读、可写、可执行,是否为普通文件,是否为目录分别对应:
@@ -257,7 +315,8 @@ $ test var1 -gt var2
$ test -r filename
```
#### 范例:字符传属性以及比较 ####
<span id="toc_19246_27800_24"></span>
#### 范例:字符传属性以及比较
> 串的长度为零:`-z`; 非零:`-n`,如:
@@ -268,7 +327,8 @@ $ test -z s1
如果串s1长度为零,返回真。
#### 范例:串比较 ####
<span id="toc_19246_27800_25"></span>
#### 范例:串比较
> 相等`"s1"="s2"` 不相等 `"s1"!="s2"`
@@ -280,6 +340,7 @@ $ if [[ 'abcde' < 'abcdf' ]]; then echo "yeah,果然是诶"; fi
yeah,果然是诶
```
<span id="toc_19246_27800_26"></span>
### 整数算术或关系运算expr
@@ -295,9 +356,11 @@ $ i=5;expr $i+5
另外,bc是一个命令行计算器,可以进行一些算术计算。
<span id="toc_19246_27800_27"></span>
### 控制执行流程命令
#### 范例:条件分支命令if ####
<span id="toc_19246_27800_28"></span>
#### 范例:条件分支命令if
if命令举例:如果第一个参数是一个普通文件名,那么分页打印该文件;否则,如果它为目录名,则进入该目录并打印该目录下的所有文件,如果也不是目录,那么提示相关信息。
@@ -315,7 +378,8 @@ else
fi
```
#### 范例:case命令举例 ####
<span id="toc_19246_27800_29"></span>
#### 范例:case命令举例
case命令是一个基于模式匹配的多路分支命令,下面将根据用户键盘输入情况决定下一步将执行那一组命令。
@@ -335,7 +399,8 @@ do
done
```
#### 范例:循环语句while, until ####
<span id="toc_19246_27800_30"></span>
#### 范例:循环语句while, until
语法:
@@ -345,11 +410,12 @@ while/until 命令表1
do
命令表2
done
```
```
区别是,前者执行命令表1后,如果退出状态为零,那么执行do后面的命令表2,然后回到起始处,而后者执行命令表1后,如果退出状态非零,才执行类似操作。 例子同上。
#### 范例:有限循环命令for ####
<span id="toc_19246_27800_31"></span>
#### 范例:有限循环命令for
语法:
@@ -373,6 +439,7 @@ do
done
```
<span id="toc_19246_27800_32"></span>
### 函数
@@ -424,6 +491,7 @@ show "$HW"
如果直接`show $HW`,肯定不行,因为`$1`只接受到了Hello,所以结果只显示Hello,原因是字符串变量必须用`"`包含起来。
<span id="toc_19246_27800_33"></span>
### 后记
感兴趣的话继续学习吧!
+20 -2
View File
@@ -1,9 +1,21 @@
# 准备工作
- [前言](#toc_22391_32127_1)
- [什么是Shell](#toc_22391_32127_2)
- [搭建运行环境](#toc_22391_32127_3)
- [基本语法介绍](#toc_22391_32127_4)
- [Shell程序设计过程](#toc_22391_32127_5)
- [调试方法介绍](#toc_22391_32127_6)
- [小结](#toc_22391_32127_7)
- [参考资料](#toc_22391_32127_8)
<span id="toc_22391_32127_1"></span>
## 前言
到最后一节来写“开篇”,确实有点古怪。不过,在[第一篇(数值操作)](http://www.tinylab.org/Shell-numeric-calculation/)的开头实际上也算是一个小的开篇,那里提到整个序列的前提是需要有一定的Shell编程基础,因此,为了能够让没有Shell编程基础的读者也可以阅读这个序列,我到最后来重写这个开篇。开篇主要介绍什么是Shell,Shell运行环境,Shell基本语法和调试技巧。
<span id="toc_22391_32127_2"></span>
## 什么是Shell
首先让我们从下图看看shell在整个操作系统中所处的位置吧,该图的外圆描述了整个操作系统(比如Debian/Ubuntu/Slackware等),内圆描述了操作系统的核心(比如Linux Kernel),而SHELL和GUI一样作为用户和操作系统之间的接口。
@@ -17,7 +29,7 @@ SHELL作为一种用户接口,它实际上是一个能够解释和分析用户
```
$ echo $SHELL
/bin/bash
$ ls -l /bin/bash
$ ls -l /bin/bash
-rwxr-xr-x 1 root root 702160 2008-05-13 02:33 /bin/bash
```
@@ -25,6 +37,7 @@ $ ls -l /bin/bash
既然该程序可以解释具有一定语法结构的文件,那么我们就可以遵循某一语法来编写它,它有什么样的语法,如何运行,如何调试呢?下面我们以Bash为例来讨论这几个方面。
<span id="toc_22391_32127_3"></span>
## 搭建运行环境
为了方便后面的练习,我们先搭建一个基本运行环境:在一个Linux操作系统中,有一个运行有Bash的命令行在等待我们键入命令,这个命令行可以是图形界面下的Terminal(例如Ubuntu下非常厉害的Terminator),也可以是字符界面的Console(可以用CTRL+ALT+F1~6切换),如果你发现当前SHELL不是Bash,请用下面的方法替换它:
@@ -73,6 +86,7 @@ $ source /path/to/test.sh
$ . /path/to/test.sh
```
<span id="toc_22391_32127_4"></span>
## 基本语法介绍
先来一个Hello, World程序。
@@ -85,7 +99,7 @@ $ . /path/to/test.sh
echo "Hello, World"
```
把上述代码保存为test.sh,然后通过上面两种不同方式运行,可以看到如下效果。
把上述代码保存为test.sh,然后通过上面两种不同方式运行,可以看到如下效果。
方法一:
@@ -118,6 +132,7 @@ Hello, World
其他语法细节请直接看[《Shell编程学习笔记》](http://www.tinylab.org/shell-programming-study-notes/)即本书后面的附录一。
<span id="toc_22391_32127_5"></span>
## Shell程序设计过程
SHELL语言作为解释型语言,它的程序设计过程跟编译型语言有些区别,其基本过程如下:
@@ -128,14 +143,17 @@ SHELL语言作为解释型语言,它的程序设计过程跟编译型语言有
可见它没有编译型语言的"麻烦的"编译和链接过程,不过正是因为这样,它出错时调试起来不是很方便,因为语法错误和逻辑错误都在运行时出现。下面我们简单介绍一下调试方法。
<span id="toc_22391_32127_6"></span>
## 调试方法介绍
可以直接参考资料:[Shell脚本调试技术](http://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/index.html)。
<span id="toc_22391_32127_7"></span>
## 小结
SHELL语言作为一门解释型语言,可以使用大量的现有工具,包括数值计算、符号处理、文件操作、网络操作等,因此,编写过程可能更加高效,但是因为它是解释型的,需要在执行过程中从磁盘上不断调用外部的程序并进行进程之间的切换,在运行效率方面可能有劣势,所以我们应该根据应用场合选择使用SHELL或是用其他的语言来编程。
<span id="toc_22391_32127_8"></span>
## 参考资料
- [《Linux命令行上程序执行的那一刹那》](http://www.cppblog.com/cuijixin/archive/2008/03/14/44463.html)
+52 -6
View File
@@ -1,5 +1,30 @@
# 数值运算
- [前言](#toc_22467_18587_1)
- [整数运算](#toc_22467_18587_2)
- [范例:对某个数加1](#toc_22467_18587_3)
- [范例:从1加到某个数](#toc_22467_18587_4)
- [范例:求模](#toc_22467_18587_5)
- [范例:求幂](#toc_22467_18587_6)
- [范例:进制转换](#toc_22467_18587_7)
- [范例:ascii字符编码](#toc_22467_18587_8)
- [浮点运算](#toc_22467_18587_9)
- [范例:求1除以13,保留3位有效数字](#toc_22467_18587_10)
- [范例:余弦值转角度](#toc_22467_18587_11)
- [范例:有一组数据,求人均月收入最高家庭](#toc_22467_18587_12)
- [随机数](#toc_22467_18587_13)
- [范例:获取一个随机数](#toc_22467_18587_14)
- [范例:随机产生一个从0到255之间的数字](#toc_22467_18587_15)
- [其他运算](#toc_22467_18587_16)
- [范例:获取一序列数](#toc_22467_18587_17)
- [范例:统计字符串中各单词出现次数](#toc_22467_18587_18)
- [范例:统计指定单词出现次数](#toc_22467_18587_19)
- [小结](#toc_22467_18587_20)
- [资料](#toc_22467_18587_21)
- [后记](#toc_22467_18587_22)
<span id="toc_22467_18587_1"></span>
## 前言
从本文开始,打算结合平时积累和进一步实践,通过一些范例来介绍Shell编程。因为范例往往能够给人以学有所用的感觉,而且给人以动手实践的机会,从而激发人的学习热情。
@@ -19,8 +44,8 @@
* 环境:如没特别说明,该系列使用的Shell将特指Bash,版本在3.1.17以上。
* 说明:该序列不是依据Shell语法组织,而是面向某些潜在的操作对象和操作本身,它们反应了现实应用。当然,在这个过程中肯定会涉及到Shell的语法。
[3]: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2198159
[4]: http://bbs.chinaunix.net/thread-218853-1-1.html
[3]: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2198159
[4]: http://bbs.chinaunix.net/thread-218853-1-1.html
[5]: http://bbs.chinaunix.net/forum.php?mod=forumdisplay&fid=24&page=1
这一篇打算讨论一下Shell编程中的基本数值运算,这类运算包括:
@@ -31,8 +56,10 @@
Shell本身可以做整数运算,复杂一些的运算要通过外部命令实现,比如expr,bc,awk等。另外,可通过RANDOM环境变量产生一个从0到32767的随机数,一些外部工具,比如awk可以通过rand()函数产生随机数。而seq命令可以用来产生一个数列。下面对它们分别进行介绍。
<span id="toc_22467_18587_2"></span>
## 整数运算
<span id="toc_22467_18587_3"></span>
### 范例:对某个数加1
```
@@ -75,6 +102,7 @@ awk is /usr/bin/awk
说明:如果要查看不同命令的帮助,对于`let``type`等Shell内置命令,可以通过Shell的一个内置命令`help`来查看相关帮助,而一些外部命令可以通过Shell的一个外部命令`man`来查看帮助,用法诸如`help let``man expr`等。
<span id="toc_22467_18587_4"></span>
### 范例:从1加到某个数
```
@@ -177,6 +205,7 @@ sys 0m2.660s
`let``expr``bc`都可以用来求模,运算符都是`%`,而`let``bc`可以用来求幂,运算符不一样,前者是`**`,后者是`^`。例如:
<span id="toc_22467_18587_5"></span>
### 范例:求模
```
@@ -195,6 +224,7 @@ $ echo $i
1
```
<span id="toc_22467_18587_6"></span>
### 范例:求幂
```
@@ -210,6 +240,7 @@ $ echo "5^2" | bc
25
```
<span id="toc_22467_18587_7"></span>
### 范例:进制转换
进制转换也是比较常用的操作,可以用Bash的内置支持也可以用bc来完成,例如把8进制的11转换为10进制,则可以:
@@ -224,6 +255,7 @@ $ echo $((8#11))
上面都是把某个进制的数转换为10进制的,如果要进行任意进制之间的转换还是bc比较灵活,因为它可以直接用ibase和obase分别指定进制源和进制转换目标。
<span id="toc_22467_18587_8"></span>
### 范例:ascii字符编码
如果要把某些字符串以特定的进制表示,可以用od命令,例如默认的分隔符IFS包括空格、TAB以及换行,可以用`man ascii`佐证。
@@ -237,10 +269,12 @@ $ echo -n "$IFS" | od -b
0000003
```
<span id="toc_22467_18587_9"></span>
## 浮点运算
`let``expr`都无法进行浮点运算,但是`bc``awk`可以。
<span id="toc_22467_18587_10"></span>
### 范例:求1除以13,保留3位有效数字
```
@@ -260,6 +294,7 @@ $ echo 1/13100 | bc -l
.00007633587786259541
```
<span id="toc_22467_18587_11"></span>
### 范例:余弦值转角度
`bc -l`计算,可以获得高精度:
@@ -277,6 +312,7 @@ $ echo 0.996293 | awk '{ printf("%s\n", atan2(sqrt(1-$1^2),$1)*180/3.1415926535)
4.93495
```
<span id="toc_22467_18587_12"></span>
### 范例:有一组数据,求人均月收入最高家庭
在这里随机产生了一组测试数据,文件名为income.txt。
@@ -294,9 +330,9 @@ $ echo 0.996293 | awk '{ printf("%s\n", atan2(sqrt(1-$1^2),$1)*180/3.1415926535)
10 5 5145
```
说明:上面的三列数据分别是家庭编号、家庭人数、家庭月总收入。
说明:上面的三列数据分别是家庭编号、家庭人数、家庭月总收入。
分析:为了求月均收入最高家庭,需要对后面两列数进行除法运算,即求出每个家庭的月均收入,然后按照月均收入排序,找出收入最高家庭。
分析:为了求月均收入最高家庭,需要对后面两列数进行除法运算,即求出每个家庭的月均收入,然后按照月均收入排序,找出收入最高家庭。
实现:
@@ -351,10 +387,12 @@ done
说明:上述脚本中还用到`seq`命令产生从1到10的一列数,这个命令的详细用法在该篇最后一节也会进一步介绍。
<span id="toc_22467_18587_13"></span>
## 随机数
环境变量`RANDOM`产生从0到32767的随机数,而`awk``rand()`函数可以产生0到1之间的随机数。
<span id="toc_22467_18587_14"></span>
### 范例:获取一个随机数
```
@@ -367,6 +405,7 @@ $ echo "" | awk '{srand(); printf("%f", rand());}'
说明:`srand()`在无参数时,采用当前时间作为`rand()`随机数产生器的一个seed。
<span id="toc_22467_18587_15"></span>
### 范例:随机产生一个从0到255之间的数字
可以通过`RANDOM`变量的缩放和`awk``rand()`的放大来实现。
@@ -412,10 +451,12 @@ done
说明:如果你的默认网关地址不是`192.168.1.1`,请自行配置`default\_gateway`(可以用`route -n`命令查看),因为用`ifconfig`配置地址时不能配置为网关地址,否则你的IP地址将和网关一样,导致整个网络不能正常工作。
<span id="toc_22467_18587_16"></span>
## 其他运算
其实通过一个循环就可以产生一序列数,但是有相关工具为什么不用呢!`seq`就是这么一个小工具,它可以产生一序列数,你可以指定数的递增间隔,也可以指定相邻两个数之间的分割符。
<span id="toc_22467_18587_17"></span>
### 范例:获取一序列数
```
@@ -482,6 +523,7 @@ $ for i in {1..5}; do echo -n "$i "; done
1 2 3 4 5
```
<span id="toc_22467_18587_18"></span>
### 范例:统计字符串中各单词出现次数
我们先给单词一个定义:由字母组成的单个或者多个字符序列。
@@ -520,6 +562,7 @@ $ cat index.html | sed -e "s/[^a-zA-Z]/\n/g" | grep -v ^$ | sort | uniq -c | sor
* `sort -n -k 1 -r`:按照第一列(`-k 1`)的数字(`-n`)逆序(`-r`)排序
* `head -10`:取出前十行
<span id="toc_22467_18587_19"></span>
### 范例:统计指定单词出现次数
可以考虑采取两种办法:
@@ -632,7 +675,8 @@ $ factor 100
```
## 小结
<span id="toc_22467_18587_20"></span>
## 小结
到这里,Shell编程范例之数值计算就结束啦。该篇主要介绍了:
@@ -645,6 +689,7 @@ $ factor 100
如果您有时间,请温习之。
<span id="toc_22467_18587_21"></span>
## 资料
* [Advanced Bash-Scripting Guide][2]
@@ -656,9 +701,10 @@ $ factor 100
* [LinuxSir.org][6]
* [ChinaUnix.net][7]
[6]: http://www.linuxsir.org/bbs/forumdisplay.php?f=60
[6]: http://www.linuxsir.org/bbs/forumdisplay.php?f=60
[7]: http://bbs.chinaunix.net/forum-24-1.html
<span id="toc_22467_18587_22"></span>
## 后记
大概花了3个多小时才写完,目前是23:33,该回宿舍睡觉啦,明天起来修改错别字和补充一些内容,朋友们晚安!
+77 -17
View File
@@ -1,18 +1,53 @@
# 布尔运算
- [前言](#toc_17877_9500_1)
- [常规的布尔运算](#toc_17877_9500_2)
- [在Shell下如何进行逻辑运算](#toc_17877_9500_3)
- [范例:true or false](#toc_17877_9500_4)
- [范例:与运算](#toc_17877_9500_5)
- [范例:或运算](#toc_17877_9500_6)
- [范例:非运算,即取反](#toc_17877_9500_7)
- [Bash里头的true和false是我们通常认为的1和0么?](#toc_17877_9500_8)
- [范例:返回值 v.s. 逻辑值](#toc_17877_9500_9)
- [范例:查看true和false帮助和类型](#toc_17877_9500_10)
- [条件测试](#toc_17877_9500_11)
- [条件测试基本使用](#toc_17877_9500_12)
- [范例:数值测试](#toc_17877_9500_13)
- [范例:字符串测试](#toc_17877_9500_14)
- [范例:文件测试](#toc_17877_9500_15)
- [各种逻辑测试的组合](#toc_17877_9500_16)
- [范例:如果a,b,c都等于下面对应的值,那么打印YES,通过-a进行与测试](#toc_17877_9500_17)
- [范例:测试某个“东西”是文件或者目录,通过-o进行“或”运算](#toc_17877_9500_18)
- [范例:测试某个“东西”是否为文件,测试`!`非运算](#toc_17877_9500_19)
- [比较-a与&&, -o与|| ! test与test !](#toc_17877_9500_20)
- [范例:要求某文件可执行且有内容,用-a和&&分别实现](#toc_17877_9500_21)
- [范例:要求某个字符串要么为空,要么和某个字符串相等](#toc_17877_9500_22)
- [范例:测试某个数字不满足指定的所有条件](#toc_17877_9500_23)
- [命令列表](#toc_17877_9500_24)
- [命令列表的执行规律](#toc_17877_9500_25)
- [范例:如果ping通www.lzu.edu.cn,那么打印连通信息](#toc_17877_9500_26)
- [命令列表的作用](#toc_17877_9500_27)
- [范例:在脚本里判断程序的参数个数,和参数类型](#toc_17877_9500_28)
- [小结](#toc_17877_9500_29)
<span id="toc_17877_9500_1"></span>
## 前言
上个礼拜介绍了[Shell编程范例之数值运算](http://www.tinylab.org/shell-numeric-calculation/),对Shell下基本数值运算方法做了简单的介绍,这周将一起探讨布尔运算,即如何操作“真假值”。
在Bash里有这样的常量(实际上是两个内置命令,在这里我们姑且这么认为,后面将介绍),即true和false,一个表示真,一个表示假。对它们可以进行与、或、非运算等常规的逻辑运算,在这一节,我们除了讨论这些基本逻辑运算外,还将讨论Shell编程中的**条件测试**和**命令列表**,并介绍它们和布尔运算的关系。
<span id="toc_17877_9500_2"></span>
## 常规的布尔运算
这里主要介绍Bash里头常规的逻辑运算,与、或、非。
<span id="toc_17877_9500_3"></span>
### 在Shell下如何进行逻辑运算
#### 范例:true or false ####
<span id="toc_17877_9500_4"></span>
#### 范例:true or false
单独测试true和false,可以看出true是真值,false为假
@@ -21,7 +56,8 @@
$ if false;then echo "YES"; else echo "NO"; fi
NO
#### 范例:与运算 ####
<span id="toc_17877_9500_5"></span>
#### 范例:与运算
$ if true && true;then echo "YES"; else echo "NO"; fi
YES
@@ -32,7 +68,8 @@
$ if false && true;then echo "YES"; else echo "NO"; fi
NO
#### 范例:或运算 ####
<span id="toc_17877_9500_6"></span>
#### 范例:或运算
$ if true || true;then echo "YES"; else echo "NO"; fi
YES
@@ -43,7 +80,8 @@
$ if false || false;then echo "YES"; else echo "NO"; fi
NO
#### 范例:非运算,即取反 ####
<span id="toc_17877_9500_7"></span>
#### 范例:非运算,即取反
$ if ! false;then echo "YES"; else echo "NO"; fi
YES
@@ -52,11 +90,13 @@
可以看出`true``false`按照我们对逻辑运算的理解进行着,但是为了能够更好的理解Shell对逻辑运算的实现,我们还得弄清楚,`true``false`是怎么工作的?
<span id="toc_17877_9500_8"></span>
### Bash里头的true和false是我们通常认为的1和0么?
回答是:否。
#### 范例:返回值 v.s. 逻辑值 ####
<span id="toc_17877_9500_9"></span>
#### 范例:返回值 v.s. 逻辑值
true和false它们本身并非逻辑值,它们都是Shell的内置命令,只是它们的返回值是一个“逻辑值”:
@@ -69,7 +109,8 @@ true和false它们本身并非逻辑值,它们都是Shell的内置命令,只
可以看到`true`返回了0,而`false`则返回了1。跟我们离散数学里学的真值1和0并不是对应的,而且相反的。
#### 范例:查看true和false帮助和类型 ####
<span id="toc_17877_9500_10"></span>
#### 范例:查看true和false帮助和类型
$ help true false
true: true
@@ -84,22 +125,26 @@ true和false它们本身并非逻辑值,它们都是Shell的内置命令,只
从上面的操作不难联想到在C语言程序设计中为什么会强调在main函数前面加上int,并在末尾加上`return 0`。因为在Shell里,将把`0`作为程序是否成功结束的标志,这就是Shell里头`true``false`的实质,它们用以反应某个程序是否正确结束,而并非传统的真假值(`1``0`),相反地,它们返回的是`0``1`。不过庆幸地是,我们在做逻辑运算时,无须关心这些。
<span id="toc_17877_9500_11"></span>
## 条件测试
从上节中,我们已经清楚地了解了Shell下的“逻辑值”是什么:是进程退出时的返回值,如果成功返回,则为真,如果不成功返回,则为假。
而条件测试正好使用了`test`这么一个指令,它用来进行数值测试(各种数值属性测试)、字符串测试(各种字符串属性测试)、文件测试(各种文件属性测试),我们通过判断对应的测试是否成功,从而完成各种常规工作,再加上各种测试的逻辑组合后,将可以完成更复杂的工作。
<span id="toc_17877_9500_12"></span>
### 条件测试基本使用
#### 范例:数值测试 ####
<span id="toc_17877_9500_13"></span>
#### 范例:数值测试
$ if test 5 -eq 5;then echo "YES"; else echo "NO"; fi
YES
$ if test 5 -ne 5;then echo "YES"; else echo "NO"; fi
NO
#### 范例:字符串测试 ####
<span id="toc_17877_9500_14"></span>
#### 范例:字符串测试
$ if test -n "not empty";then echo "YES"; else echo "NO"; fi
YES
@@ -110,36 +155,43 @@ true和false它们本身并非逻辑值,它们都是Shell的内置命令,只
$ if test -n "";then echo "YES"; else echo "NO"; fi
NO
#### 范例:文件测试 ####
<span id="toc_17877_9500_15"></span>
#### 范例:文件测试
$ if test -f /boot/System.map; then echo "YES"; else echo "NO"; fi
YES
$ if test -d /boot/System.map; then echo "YES"; else echo "NO"; fi
NO
<span id="toc_17877_9500_16"></span>
### 各种逻辑测试的组合
#### 范例:如果a,b,c都等于下面对应的值,那么打印YES,通过-a进行"与"测试 ####
<span id="toc_17877_9500_17"></span>
#### 范例:如果a,b,c都等于下面对应的值,那么打印YES,通过-a进行"与"测试
$ a=5;b=4;c=6;
$ if test $a -eq 5 -a $b -eq 4 -a $c -eq 6; then echo "YES"; else echo "NO"; fi
YES
#### 范例:测试某个“东西”是文件或者目录,通过-o进行“或”运算 ####
<span id="toc_17877_9500_18"></span>
#### 范例:测试某个“东西”是文件或者目录,通过-o进行“或”运算
$ if test -f /etc/profile -o -d /etc/profile;then echo "YES"; else echo "NO"; fi
YES
#### 范例:测试某个“东西”是否为文件,测试`!`非运算 ####
<span id="toc_17877_9500_19"></span>
#### 范例:测试某个“东西”是否为文件,测试`!`非运算
$ if test ! -f /etc/profile; then echo "YES"; else echo "NO"; fi
NO
上面仅仅演示了`test`命令一些非常简单的测试,你可以通过`help test`获取test的更多用法。需要注意的是,`test`命令内部的逻辑运算和Shell的逻辑运算符有一些区别,对应的为`-a``&&``-o``||`,这两者不能混淆使用。而非运算都是`!`,下面对它们进行比较。
<span id="toc_17877_9500_20"></span>
### 比较-a与&&, -o与|| ! test与test !
#### 范例:要求某文件可执行且有内容,用-a和&&分别实现 ####
<span id="toc_17877_9500_21"></span>
#### 范例:要求某文件可执行且有内容,用-a和&&分别实现
$ cat > test.sh
#!/bin/bash
@@ -151,7 +203,8 @@ true和false它们本身并非逻辑值,它们都是Shell的内置命令,只
$ if test -s test.sh && test -x test.sh; then echo "YES"; else echo "NO"; fi
YES
#### 范例:要求某个字符串要么为空,要么和某个字符串相等 ####
<span id="toc_17877_9500_22"></span>
#### 范例:要求某个字符串要么为空,要么和某个字符串相等
$ str1="test"
$ str2="test"
@@ -160,7 +213,8 @@ true和false它们本身并非逻辑值,它们都是Shell的内置命令,只
$ if test -z "$str2" || test "$str2" == "$str1"; then echo "YES"; else echo "NO"; fi
YES
#### 范例:测试某个数字不满足指定的所有条件 ####
<span id="toc_17877_9500_23"></span>
#### 范例:测试某个数字不满足指定的所有条件
$ i=5
$ if test ! $i -lt 5 -a $i -ne 6; then echo "YES"; else echo "NO"; fi
@@ -228,23 +282,28 @@ true和false它们本身并非逻辑值,它们都是Shell的内置命令,只
正是基于逻辑运算特有的性质,我们可以通过`&&``||`来取代`if/then`等条件分支结构,这样就产生了命令列表。
<span id="toc_17877_9500_24"></span>
## 命令列表
<span id="toc_17877_9500_25"></span>
### 命令列表的执行规律
命令列表的执行规律符合逻辑运算的运算规律,用`&&`连接起来的命令,如果前者成功返回,将执行后面的命令,反之不然;用`||`连接起来的命令,如果前者成功返回,将不执行后续命令,反之不然。
#### 范例:如果ping通www.lzu.edu.cn,那么打印连通信息 ####
<span id="toc_17877_9500_26"></span>
#### 范例:如果ping通www.lzu.edu.cn,那么打印连通信息
$ ping -c 1 www.lzu.edu.cn -W 1 && echo "=======connected======="
非常有趣的问题出来了,即我们上面已经提到的:为什么要让C程序在`main()`函数的最后返回0?如果不这样,把这种程序放入命令列表会有什么样的结果?你自己写个简单的C程序,然后放入命令列表看看。
<span id="toc_17877_9500_27"></span>
### 命令列表的作用
有时用命令列表取代if/then等条件分支结构可以省掉一些代码,而且使得程序比较美观、易读,例如:
#### 范例:在脚本里判断程序的参数个数,和参数类型 ####
<span id="toc_17877_9500_28"></span>
#### 范例:在脚本里判断程序的参数个数,和参数类型
#!/bin/bash
@@ -268,6 +327,7 @@ true和false它们本身并非逻辑值,它们都是Shell的内置命令,只
这样处理后,对程序参数的判断仅仅需要简单的一行代码,而且变得更美观。
<span id="toc_17877_9500_29"></span>
## 小结
这一节介绍了Shell编程中的逻辑运算,条件测试和命令列表。
+131 -37
View File
@@ -1,5 +1,54 @@
# 字符串操作
- [前言](#toc_1903_12123_1)
- [字符串的属性](#toc_1903_12123_2)
- [字符串的类型](#toc_1903_12123_3)
- [范例:数字或者数字组合](#toc_1903_12123_4)
- [范例:字符组合(小写字母、大写字母、两者的组合)](#toc_1903_12123_5)
- [范例:字母和数字的组合](#toc_1903_12123_6)
- [范例:空格或者Tab键等](#toc_1903_12123_7)
- [范例:匹配邮件地址](#toc_1903_12123_8)
- [范例:匹配URL地址(以http链接为例)](#toc_1903_12123_9)
- [范例:判断字符是否为可打印字符](#toc_1903_12123_10)
- [字符串的长度](#toc_1903_12123_11)
- [范例:计算某个字符串的长度](#toc_1903_12123_12)
- [范例:计算某些指定字符或者字符组合的个数](#toc_1903_12123_13)
- [范例:统计单词个数](#toc_1903_12123_14)
- [字符串的显示](#toc_1903_12123_15)
- [范例:在屏幕控制字符显示位置、颜色、背景等](#toc_1903_12123_16)
- [范例:在屏幕的某个位置动态显示当前系统时间](#toc_1903_12123_17)
- [范例:过滤掉某些控制字符串](#toc_1903_12123_18)
- [字符串的存储](#toc_1903_12123_19)
- [范例:把字符串拆分成字符串数组](#toc_1903_12123_20)
- [字符串常规操作](#toc_1903_12123_21)
- [取子串](#toc_1903_12123_22)
- [范例:按照位置取子串](#toc_1903_12123_23)
- [范例:匹配字符求子串](#toc_1903_12123_24)
- [查询子串](#toc_1903_12123_25)
- [范例:查询子串在目标串中的位置](#toc_1903_12123_26)
- [范例:查询子串,返回包含子串的行](#toc_1903_12123_27)
- [子串替换](#toc_1903_12123_28)
- [范例:把变量var中的空格替换成下划线](#toc_1903_12123_29)
- [插入子串](#toc_1903_12123_30)
- [范例:在var字符串的空格之前或之后插入一个下划线](#toc_1903_12123_31)
- [删除子串](#toc_1903_12123_32)
- [范例:把var字符串中所有的空格给删除掉。](#toc_1903_12123_33)
- [子串比较](#toc_1903_12123_34)
- [子串排序](#toc_1903_12123_35)
- [子串进制转换](#toc_1903_12123_36)
- [子串编码转换](#toc_1903_12123_37)
- [字符串操作进阶](#toc_1903_12123_38)
- [正则表达式](#toc_1903_12123_39)
- [范例:处理URL地址](#toc_1903_12123_40)
- [范例:匹配某个文件中的特定范围的行](#toc_1903_12123_41)
- [处理格式化的文本](#toc_1903_12123_42)
- [范例:选取指定列](#toc_1903_12123_43)
- [范例:文件关联操作](#toc_1903_12123_44)
- [参考资料](#toc_1903_12123_45)
- [后记](#toc_1903_12123_46)
<span id="toc_1903_12123_1"></span>
## 前言
忙活了一个礼拜,终于等到周末,可以空下来写点东西。
@@ -29,13 +78,16 @@
- 对串的一些比较复杂而有趣的操作,这里将在最后介绍一些有趣的范例。
<span id="toc_1903_12123_2"></span>
## 字符串的属性
### 字符串的类型 ###
<span id="toc_1903_12123_3"></span>
### 字符串的类型
字符有可能是数字、字母、空格、其他特殊字符,而字符串有可能是它们中的一种或者多种的组合,在组合之后还可能形成具有特定意义的字符串,诸如邮件地址,URL地址等。
#### 范例:数字或者数字组合 ####
<span id="toc_1903_12123_4"></span>
#### 范例:数字或者数字组合
$ i=5;j=9423483247234;
$ echo $i | grep -q "^[0-9]$"
@@ -45,31 +97,36 @@
$ echo $?
0
#### 范例:字符组合(小写字母、大写字母、两者的组合) ####
<span id="toc_1903_12123_5"></span>
#### 范例:字符组合(小写字母、大写字母、两者的组合)
$ c="A"; d="fwefewjuew"; e="fewfEFWefwefe"
$ echo $c | grep -q "^[A-Z]$"
$ echo $d | grep -q "^[a-z]\+$"
$ echo $e | grep -q "^[a-zA-Z]\+$"
#### 范例:字母和数字的组合 ####
<span id="toc_1903_12123_6"></span>
#### 范例:字母和数字的组合
$ ic="432fwfwefeFWEwefwef"
$ echo $ic | grep -q "^[0-9a-zA-Z]\+$"
#### 范例:空格或者Tab键等 ####
<span id="toc_1903_12123_7"></span>
#### 范例:空格或者Tab键等
$ echo " " | grep " "
$ echo -e "\t" | grep "[[:space:]]" #[[:space:]]会同时匹配空格和TAB键
$ echo -e " \t" | grep "[[:space:]]"
$ echo -e "\t" | grep "" #为在键盘上按下TAB键,而不是字符
#### 范例:匹配邮件地址 ####
<span id="toc_1903_12123_8"></span>
#### 范例:匹配邮件地址
$ echo "test2007@lzu.cn" | grep "[0-9a-zA-Z\.]*@[0-9a-zA-Z\.]"
test2007@lzu.cn
#### 范例:匹配URL地址(以http链接为例) ####
<span id="toc_1903_12123_9"></span>
#### 范例:匹配URL地址(以http链接为例)
$ echo "http://news.lzu.edu.cn/article.jsp?newsid=10135" | grep "^http://[0-9a-zA-Z\./=?]\+$"
http://news.lzu.edu.cn/article.jsp?newsid=10135
@@ -81,7 +138,8 @@
- 上面都是用`grep`来进行模式匹配,实际上`sed``awk`都可用来做模式匹配,关于匹配中用到的正则表达式知识,请参考后面的相关资料
- 如果想判断字符串是否为空,可判断其长度是否为零,可通过`test`命令的`-z`选项来实现,具体用法见test命令,`man test`
#### 范例:判断字符是否为可打印字符 ####
<span id="toc_1903_12123_10"></span>
#### 范例:判断字符是否为可打印字符
$ echo "\t\n" | grep "[[:print:]]"
\t\n
@@ -91,13 +149,15 @@
$ echo $?
1
<span id="toc_1903_12123_11"></span>
### 字符串的长度
除了组成字符串的字符类型外,字符串还有哪些属性呢?组成字符串的字符个数。
下面我们来计算字符串的长度,即所有字符的个数,并简单介绍几种求字符串中指定字符个数的方法。
#### 范例:计算某个字符串的长度 ####
<span id="toc_1903_12123_12"></span>
#### 范例:计算某个字符串的长度
即计算所有字符的个数,计算方法五花八门,择其优着而用之:
@@ -113,7 +173,8 @@
$ echo -n $var | wc -c
20
#### 范例:计算某些指定字符或者字符组合的个数 ####
<span id="toc_1903_12123_13"></span>
#### 范例:计算某些指定字符或者字符组合的个数
$ echo $var | tr -cd g | wc -c
2
@@ -122,7 +183,8 @@
$ echo -n $var | sed -e 's/[^gt]//g' | wc -c
5
#### 范例:统计单词个数 ####
<span id="toc_1903_12123_14"></span>
#### 范例:统计单词个数
更多相关信息见[《数值计算》](http://www.tinylab.org/shell-numeric-calculation/)的*单词统计* 相关范例。
@@ -131,26 +193,30 @@
$ echo "$var" | tr " " "\n" | grep get | uniq -c
1
$ echo "$var" | tr " " "\n" | grep get | wc -l
1
1
说明:
`${}`操作符在Bash里头是一个“大牛”,能胜任相当多的工作,具体就看网中人的[《Shell十三问》之`$(())`与`$()` 还有`${}`差在哪?"](http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=218853&page=7#pid1617953)吧。
<span id="toc_1903_12123_15"></span>
## 字符串的显示
接下来讨论如何控制字符在终端的显示。
### 范例:在屏幕控制字符显示位置、颜色、背景等 ####
<span id="toc_1903_12123_16"></span>
### 范例:在屏幕控制字符显示位置、颜色、背景等
$ echo -e "\033[31;40m" #设置前景色为黑色,背景色为红色
$ echo -e "\033[11;29H Hello, World\!" #在屏幕的第11行29列开始打印字符串Hello,World!
### 范例:在屏幕的某个位置动态显示当前系统时间 ####
<span id="toc_1903_12123_17"></span>
### 范例:在屏幕的某个位置动态显示当前系统时间
$ while :; do echo -e "\033[11;29H "$(date "+%Y-%m-%d %H:%M:%S"); done
### 范例:过滤掉某些控制字符串 ####
<span id="toc_1903_12123_18"></span>
### 范例:过滤掉某些控制字符串
用col命令过滤某些控制字符,在处理诸如script,screen等截屏命令的输出结果时,很有用。
@@ -159,6 +225,7 @@
$ exit
$ cat screenlog.0 | col -b # 把一些控制字符过滤后,就可以保留可读的操作日志
<span id="toc_1903_12123_19"></span>
## 字符串的存储
在我们看来,字符串是一连串的字符而已,但是为了操作方便,我们往往可以让字符串呈现出一定的结构。在这里,我们不关心字符串在内存中的实际存储结构,仅仅关系它呈现出来的逻辑结构。比如,这样一个字符串:"get
@@ -178,6 +245,7 @@ the length of me",我们可以从不同的方面来呈现它。
同样是用到分割符,但为了更方便的操作分割以后的字符串的各个部分,我们抽象了“数组”这么一个数据结构,从而让我们更加方便地通过下标来获取某个指定的部分。bash提供了这么一种数据结构,而优秀的awk也同样提供了它,我们这里将简单介绍它们的用法。
<span id="toc_1903_12123_20"></span>
### 范例:把字符串拆分成字符串数组
- Bash提供的数组数据结构,以数字为下标的,和C语言从0开始的下标一样
@@ -222,31 +290,33 @@ Bash实际上还提供了一种类似于“数组”的功能,即"for i in"
awk的“数组”功能何止于此呢,看看它的for引用吧,注意,这个和Bash里头的for不太一样,i不是元素本身,而是下标:
$ echo $var | awk '{split($0, var_arr, " "); for(i in var_arr) printf("%s ",var_arr[i]);}'
of me get the length
4 5 1 2 3
of me get the length
4 5 1 2 3
另外,从上述结果可以看到,经过for处理后,整个结果没有按照原理的字符顺序排列,不过如果仅仅是迭代出所有元素这个同样很有意义。
awk还有更“厉害”的处理能力,它的下标可以不是数字,可以是字符串,从而变成了“关联”数组,这种“关联”在某些方面非常方便。
比如,把某个文件中的某个系统调用名根据另外一个文件中的函数地址映射表替换成地址,可以这么实现:
$ cat symbol
$ cat symbol
sys_exit
sys_read
sys_close
$ ls /boot/System.map*
$ awk '{if(FILENAME ~ "System.map") map[$3]=$1; else {printf("%s\n", map[$1])}}' \
/boot/System.map-2.6.20-16-generic symbol
/boot/System.map-2.6.20-16-generic symbol
c0129a80
c0177310
c0175d80
另外,awk还支持用delete函数删除某个数组元素。如果某些场合有需要的话,别忘了awk还支持二维数组。
<span id="toc_1903_12123_21"></span>
## 字符串常规操作
字符串操作包括取子串、查询子串、插入子串、删除子串、子串替换、子串比较、子串排序、子串进制转换、子串编码转换等。
<span id="toc_1903_12123_22"></span>
### 取子串
取子串的方法主要有:
@@ -254,7 +324,8 @@ awk还有更“厉害”的处理能力,它的下标可以不是数字,可
- 直接到指定位置求子串
- 字符匹配求子串
#### 范例:按照位置取子串 ####
<span id="toc_1903_12123_23"></span>
#### 范例:按照位置取子串
比如从什么位置开始,取多少个字符
@@ -281,12 +352,13 @@ awk把`$var`按照空格分开为多个变量,依次为`$1`,`$2`,`$3`,`$4`,`$5
$ echo $var | cut -d" " -f 5
#### 范例:匹配字符求子串 ####
<span id="toc_1903_12123_24"></span>
#### 范例:匹配字符求子串
用Bash内置支持求字串:
$ echo ${var%% *} #从右边开始计算,删除最左边的空格右边的所有字符
get
get
$ echo ${var% *} #从右边开始计算,删除第一个空格右边的所有字符
get the length of
$ echo ${var##* } #从左边开始计算,删除最右边的空格左边的所有字符
@@ -327,6 +399,7 @@ tr也可以用来取子串,它可以类似`#`和`%`来“拿掉”一些字符
$ echo -n "abcdefghijk" | tail -c 4
hijk
<span id="toc_1903_12123_25"></span>
### 查询子串
子串查询包括:
@@ -336,7 +409,8 @@ tr也可以用来取子串,它可以类似`#`和`%`来“拿掉”一些字符
准备:在进行下面的操作之前,请准备一个文件test.txt,里头有内容"consists of",用于下面的范例。
#### 范例:查询子串在目标串中的位置 ####
<span id="toc_1903_12123_26"></span>
#### 范例:查询子串在目标串中的位置
`expr index`貌似仅仅可以返回某个字符或者多个字符中第一个字符出现的位置
@@ -349,7 +423,8 @@ awk却能找出字串,match还可以匹配正则表达式
$ echo $var | awk '{printf("%d\n", match($0,"the"));}'
5
#### 范例:查询子串,返回包含子串的行 ####
<span id="toc_1903_12123_27"></span>
#### 范例:查询子串,返回包含子串的行
`awk``sed`都可以实现这些功能,但是`grep`最擅长
@@ -365,11 +440,13 @@ awk却能找出字串,match还可以匹配正则表达式
- `awk``grep``sed`都能通过模式匹配查找指定字符串,但它们各有所长,将在后续章节中继续使用和比较它们,进而发现各自优点
- 在这里姑且把文件内容当成了一个大的字符串,在后面章节中将专门介绍文件操作,所以对文件内容中存放字符串的操作将会有更深入的分析和介绍
<span id="toc_1903_12123_28"></span>
### 子串替换
子串替换就是把某个指定的子串替换成其他的字符串,这里蕴含了“插入子串”和“删除子串”的操作。例如,想插入某个字符串到某个子串之前,就可以把原来的子串替换成”子串+新的字符串“,如果想删除某个子串,就把子串替换成空串。不过有些工具提供了一些专门的用法来做插入子串和删除子串的操作,所以呆伙还会专门介绍。另外,要想替换掉某个子串,一般都是先找到子串(查询子串),然后再把它替换掉,实质上很多工具在使用和设计上都体现了这么一点。
#### 范例:把变量var中的空格替换成下划线 ####
<span id="toc_1903_12123_29"></span>
#### 范例:把变量var中的空格替换成下划线
`{}`运算符,还记得么?网中人的教程
@@ -404,13 +481,15 @@ awk却能找出字串,match还可以匹配正则表达式
有一种比较有意思的字符串替换是:整个文件行的倒置,这个可以通过tac命令实现,它会把文件中所有的行全部倒转过来。在某种意义上来说,排序实际上也是一个字符串替换。
<span id="toc_1903_12123_30"></span>
### 插入子串
在指定位置插入子串,这个位置可能是某个子串的位置,也可能是从某个文件开头算起的某个长度。通过上面的练习,我们发现这两者之间实际上是类似的。
公式:插入子串=把"old子串"替换成"old子串+new子串"或者"new子串+old子串"
#### 范例:在var字符串的空格之前或之后插入一个下划线 ####
<span id="toc_1903_12123_31"></span>
#### 范例:在var字符串的空格之前或之后插入一个下划线
用{}
@@ -436,7 +515,7 @@ awk却能找出字串,match还可以匹配正则表达式
$ echo $var | sed -e 's/\( \)/\1_/g'
get _the _length _of _me
看看`sed`的标签的顺序是不是`\1`,`\2`....,看到没?`\2``\1`调换位置后,the和get的位置掉换了:
看看`sed`的标签的顺序是不是`\1`,`\2`....,看到没?`\2``\1`调换位置后,the和get的位置掉换了:
$ echo $var | sed -e 's/\([a-z]*\) \([a-z]*\) /\2 \1 /g'
the get of length me
@@ -450,17 +529,19 @@ awk却能找出字串,match还可以匹配正则表达式
test
get the length of me
<span id="toc_1903_12123_32"></span>
### 删除子串
删除子串:应该很简单了吧,把子串替换成“空”(什么都没有)不就变成了删除么。还是来简单复习一下替换吧。
<span id="toc_1903_12123_33"></span>
#### 范例:把var字符串中所有的空格给删除掉。
鼓励:这样一替换不知道变成什么单词啦,谁认得呢?但是中文却是连在一起的,所以中文有多难,你想到了么?原来你也是个语言天才,而英语并不可怕,你有学会它的天赋,只要有这个打算。
再用{}
$ echo ${var// /}
$ echo ${var// /}
getthelengthofme
再用awk
@@ -479,10 +560,12 @@ awk却能找出字串,match还可以匹配正则表达式
如果要删除第一个空格后面所有的字符串该怎么办呢?还记得`{}``#``%`用法么?如果不记得,回到这节的开头开始复习吧。(实际上删除子串和取子串未尝不是两种互补的运算呢,删除掉某些不想要的子串,也就同时取得另外那些想要的子串——这个世界就是一个“二元”的世界,非常有趣)
<span id="toc_1903_12123_34"></span>
### 子串比较
这个很简单:还记得test命令的用法么?`man test`。它可以用来判断两个字符串是否相等。另外,有发现“字符串是否相等”和“字符串能否跟另外一个字符串匹配"两个问题之间的关系吗?如果两个字符串完全匹配,那么这两个字符串就相等了。所以呢,上面用到的字符串匹配方法,也同样可以用到这里。
<span id="toc_1903_12123_35"></span>
### 子串排序
差点忘记这个重要内容了,子串排序可是经常用到,常见的有按字母序、数字序等正序或反序排列。sort命令可以用来做这个工作,它和其他行处理命令一样,是按行操作的,另外,它类似cut和awk,可以指定分割符,并指定需要排序的列。
@@ -519,6 +602,7 @@ awk却能找出字串,match还可以匹配正则表达式
41 45 44 44 26 44 42 20 20 38 37 25 45 45 45
46 47 48 49 50 51 52 53 54 55 56
<span id="toc_1903_12123_36"></span>
### 子串进制转换
如果字母和数字字符用来计数,那么就存在进制转换的问题。在《数值计算》(http://www.tinylab.org/shell-numeric-calculation/)一节,已经介绍了bc命令,这里再复习一下。
@@ -528,22 +612,26 @@ awk却能找出字串,match还可以匹配正则表达式
说明:ibase指定输入进制,obase指出输出进制,这样通过调整ibase和obase,你想怎么转就怎么转啦!
<span id="toc_1903_12123_37"></span>
### 子串编码转换
什么是字符编码?这个就不用介绍了吧,看过那些乱七八糟显示的网页么?大多是因为浏览器显示时的”编码“和网页实际采用的”编码“不一致导致的。字符编码通常是指:把一序列”可打印“字符转换成二进制表示,而字符解码呢则是执行相反的过程,如果这两个过程不匹配,则出现了所谓的”乱码“。
为了解决”乱码“问题呢?就需要进行编码转换。在Linux下,我们可以使用`iconv`这个工具来进行相关操作。这样的情况经常在不同的操作系统之间移动文件,不同的编辑器之间交换文件的时候遇到,目前在Windows下常用的汉字编码是gb2312,而在Linux下则大多采用utf8。
$ nihao_utf8=$(echo "你好")
$ nihao_utf8=$(echo "你好")
$ nihao_gb2312=$(echo $nihao_utf8 | iconv -f utf8 -t gb2312)
<span id="toc_1903_12123_38"></span>
## 字符串操作进阶
实际上,在用Bash编程时,大部分时间都是在处理字符串,因此把这一节熟练掌握非常重要。
<span id="toc_1903_12123_39"></span>
### 正则表达式
#### 范例:处理URL地址 ####
<span id="toc_1903_12123_40"></span>
#### 范例:处理URL地址
URL地址(URLUniform Resoure Locator:统一资源定位器)是WWW页的地址)几乎是我们日常生活的玩伴,我们已经到了无法离开它的地步啦,对它的操作很多,包括判断URL地址的有效性,截取地址的各个部分(服务器类型、服务器地址、端口、路径等)并对各个部分进行进一步的操作。
@@ -584,7 +672,8 @@ URL地址(URLUniform Resoure Locator:统一资源定位器)是WWW页的
$ echo $url | sed -e 's/.*[0-9].\(.*\)/\1/g'
tar.gz
#### 范例:匹配某个文件中的特定范围的行 ####
<span id="toc_1903_12123_41"></span>
#### 范例:匹配某个文件中的特定范围的行
先准备一个测试文件README
@@ -597,7 +686,7 @@ URL地址(URLUniform Resoure Locator:统一资源定位器)是WWW页的
7.2 it will depend on the exection mode, interactive or redirection to a file,
if interactive, the "output" action will accur after the \n char with the line
buffer mode, else, it will be really "printed" after all of the strings have
been stayed in the buffer.
been stayed in the buffer.
7.3 there is no another effective method in most OS. because argc and argv are
not global variables like environ.
@@ -613,14 +702,15 @@ URL地址(URLUniform Resoure Locator:统一资源定位器)是WWW页的
其实,因为这个文件内容格式很有特色,有更简单的办法
$ awk '/7.2/,/^$/ {printf("%s\n", $0);}' README
$ awk '/7.2/,/^$/ {printf("%s\n", $0);}' README
7.2 it will depend on the exection mode, interactive or redirection to a file,
if interactive, the "output" action will accur after the \n char with the line
buffer mode, else, it will be really "printed" after all of the strings have
been stayed in the buffer.
been stayed in the buffer.
有了上面的知识,就可以非常容易地进行这些工作啦:修改某个文件的文件名,比如调整它的编码,下载某个网页里头的所有pdf文档等。这些就作为练习自己做吧。
<span id="toc_1903_12123_42"></span>
### 处理格式化的文本
平时做工作,大多数时候处理的都是一些“格式化”的文本,比如类似`/etc/passwd`这样的有固定行和列的文本,也有类似`tree`命令输出的那种具有树形结构的文本,当然还有其他具有特定结构的文本。
@@ -631,7 +721,8 @@ URL地址(URLUniform Resoure Locator:统一资源定位器)是WWW页的
下面来介绍具体文本的操作,以`/etc/passwd`文件为例。关于这个文件的帮忙和用法,请通过`man 5 passwd`查看。下面对这个文件以及相关的文件进行一些有意义的操作。
#### 范例:选取指定列 ####
<span id="toc_1903_12123_43"></span>
#### 范例:选取指定列
选取/etc/passwd文件中的用户名和组ID两列
@@ -641,7 +732,8 @@ URL地址(URLUniform Resoure Locator:统一资源定位器)是WWW页的
$ cat /etc/group | cut -d":" -f1,3
#### 范例:文件关联操作 ####
<span id="toc_1903_12123_44"></span>
#### 范例:文件关联操作
如果想找出所有用户所在的组,怎么办?
@@ -693,7 +785,7 @@ URL地址(URLUniform Resoure Locator:统一资源定位器)是WWW页的
转换成:
$ cat a
root bin daemon
root bin daemon
0 1 2
并转换回去,有什么办法呢?记得诸如tr,paste,split等命令都可以使用。
@@ -703,6 +795,7 @@ URL地址(URLUniform Resoure Locator:统一资源定位器)是WWW页的
- 正转换:先截取用户名一列存入文件user,再截取用户ID存入id,再把两个文件用`paste -s`命令连在一起,这样就完成了正转换
- 逆转换:先把正转换得到的结果用`split -1`拆分成两个文件,再把两个拆分后的文件用tr把分割符`\t`替换成`\n`,只有用`paste`命令把两个文件连在一起,这样就完成了逆转换。
<span id="toc_1903_12123_45"></span>
## 参考资料
- 《高级Bash脚本编程指南》之操作字符串
@@ -724,6 +817,7 @@ URL地址(URLUniform Resoure Locator:统一资源定位器)是WWW页的
- [用Shell写的五笔反查小工具](http://oss.lzu.edu.cn/old/modules/newbb/viewtopic.php?topic_id=574&forum=26&post_id=2378#forumpost2378)
- [SED单行脚本快速参考(Unix流编辑器](http://sed.sourceforge.net/sed1line_zh-CN.html)
<span id="toc_1903_12123_46"></span>
## 后记
- 这一节本来是上个礼拜该弄好的,但是这些天太忙了,到现在才写好一个“初稿”,等到有时间再补充具体的范例。这一节的范例应该是最最有趣的,所有得好好研究一下几个有趣的范例。
+78 -16
View File
@@ -1,5 +1,38 @@
# 文件操作
- [前言](#toc_11738_32168_1)
- [文件的各种属性](#toc_11738_32168_2)
- [文件类型](#toc_11738_32168_3)
- [范例:在命令行简单地区分各类文件](#toc_11738_32168_4)
- [范例:简单比较它们的异同](#toc_11738_32168_5)
- [范例:普通文件再分类](#toc_11738_32168_6)
- [文件属主](#toc_11738_32168_7)
- [范例:修改文件的属主](#toc_11738_32168_8)
- [范例:查看文件的属主](#toc_11738_32168_9)
- [范例:分析文件属主实现的背后原理](#toc_11738_32168_10)
- [文件权限](#toc_11738_32168_11)
- [范例:给文件添加读、写、可执行权限](#toc_11738_32168_12)
- [范例:授权普通用户执行root所属命令](#toc_11738_32168_13)
- [范例:给重要文件加锁](#toc_11738_32168_14)
- [文件大小](#toc_11738_32168_15)
- [范例:查看普通文件和链接文件](#toc_11738_32168_16)
- [范例:查看设备文件](#toc_11738_32168_17)
- [范例:查看目录](#toc_11738_32168_18)
- [文件访问、更新、修改时间](#toc_11738_32168_19)
- [文件名](#toc_11738_32168_20)
- [文件的基本操作](#toc_11738_32168_21)
- [范例:创建文件](#toc_11738_32168_22)
- [范例:删除文件](#toc_11738_32168_23)
- [范例:复制文件](#toc_11738_32168_24)
- [范例:修改文件名](#toc_11738_32168_25)
- [范例:编辑文件](#toc_11738_32168_26)
- [范例:压缩/解压缩文件](#toc_11738_32168_27)
- [范例:文件搜索(文件定位)](#toc_11738_32168_28)
- [参考资料](#toc_11738_32168_29)
- [后记](#toc_11738_32168_30)
<span id="toc_11738_32168_1"></span>
## 前言
这周来探讨文件操作。
@@ -10,6 +43,7 @@
下面先来介绍文件的各种属性,然后介绍普通文件的一般操作。
<span id="toc_11738_32168_2"></span>
## 文件的各种属性
首先通过文件的结构体来看看文件到底有哪些属性:
@@ -32,11 +66,13 @@
下面逐次来了解这些属性,如果需要查看某个文件属性,用`stat`命令就可,它会按照上面的结构体把信息列出来。另外,`ls`命令在跟上一定参数后也可以显示文件的相关属性,比如`-l`参数。
<span id="toc_11738_32168_3"></span>
### 文件类型
文件类型对应于上面的st\_mode, 文件类型有很多,比如常规文件、符号链接(硬链接、软链接)、管道文件、设备文件(符号设备、块设备)、socket文件等,不同的文件类型对应不同的功能和作用。
#### 范例:在命令行简单地区分各类文件 ####
<span id="toc_11738_32168_4"></span>
#### 范例:在命令行简单地区分各类文件
$ ls -l
total 12
@@ -66,7 +102,8 @@
说明:通过`ls`命令结果每行的第一个字符可以看到,它们之间都不相同,这正好反应了不同文件的类型。`d`表示目录,`-`表示普通文件(或者硬链接),`l`表示符号链接,`p`表示管道文件,`b``c`分别表示块设备和字符设备(另外`s`表示socket文件)。在stat命令的结果中,可以在第二行的最后找到说明,从上面的操作可以看出,directory\_file是目录,stat命令的结果中用directory表示,而null\_char\_dev\_file它则用character special file说明。
#### 范例:简单比较它们的异同 ####
<span id="toc_11738_32168_5"></span>
#### 范例:简单比较它们的异同
通常只会用到目录、普通文件、以及符号链接,很少碰到其他类型的文件,不过这些文件还是各有用处的,如果要做嵌入式开发或者进程通信等,可能会涉及到设备文件、有名管道(FIFO)。下面通过简单的操作来反应它们之间的区别(具体原理会在下一节《shell编程范例之文件系统》介绍,如果感兴趣,也可以提前到网上找找设备文件的作用、块设备和字符设备的区别、以及驱动程序中如何编写相关设备驱动等)。
@@ -78,7 +115,7 @@
在目录中可以创建新文件,所以目录还有叫法:文件夹,到后面会分析目录文件的结构体,它实际上存放了它下面的各个文件的文件名。
$ cd directory_file
$ cd directory_file
$ touch file1 file2 file3
对于有名管道,操作起来比较有意思:如果要读它,除非有内容,否则阻塞;如果要写它,除非有人来读,否则阻塞。它常用于进程通信中。可以打开两个终端terminal1和terminal2,试试看:
@@ -102,14 +139,15 @@
$ rm regular_file # 删除原文件
$ cat regular_file_hard_link # 硬链接还在,而且里头的内容还有呢
fefe
$ cat regular_file_soft_link
$ cat regular_file_soft_link
cat: regular_file_soft_link: No such file or directory
虽然软链接文件本身还在,不过因为它本身不存储内容,所以读不到东西,这就是软链接和硬链接的区别。
需要注意的是,硬链接不可以跨文件系统,而软链接则可以。另外,也不允许给目录创建硬链接。
#### 范例:普通文件再分类 ####
<span id="toc_11738_32168_6"></span>
#### 范例:普通文件再分类
文件类型从Linux文件系统那么一个级别分了以上那么多类型,不过普通文件还是可以再分的(根据文件内容的”数据结构“分),比如常见的文本文件,可执行的ELF文件,odt文档,jpg图片格式,swap分区文件,pdf文件。除了文本文件外,它们大多是二进制文件,有特定的结构,因此需要有专门的工具来创建和编辑它们。关于各类文件的格式,可以参考相关文档标准。不过非常值得深入了解Linux下可执行的ELF文件的工作原理,如果有兴趣,建议阅读一下参考资料中和ELF文件相关部分,这一部分对于嵌入式Linux工程师至关重要。
@@ -143,13 +181,15 @@
更多用法见file命令的手册,关于file命令的实现原理,请参考magic的手册(看看/etc/file/magic文件,了解什么是文件的magic number等)。
<span id="toc_11738_32168_7"></span>
### 文件属主
Linux作为一个多用户系统,为多用户使用同一个系统提供了极大的方便,比如对于系统上的文件,它通过属主来区分不同的用户,以便分配它们对不同文件的操作权限。为了更方便地管理,文件属主包括该文件所属用户,以及该文件所属的用户组,因为用户可以属于多个组。先来简单介绍Linux下用户和组的管理。
Linux下提供了一组命令用于管理用户和组,比如用于创建用户的useradd和groupadd,用于删除用户的userdel和groupdel,另外,passwd命令用于修改用户密码。当然,Linux还提供了两个相应的配置,即/etc/passwd和/etc/group,另外,有些系统还把密码单独放到了配置文件/etc/shadow中。关于它们的详细用法请参考后面的资料,这里不再介绍,仅介绍文件和用户之间的一些关系。
#### 范例:修改文件的属主 ####
<span id="toc_11738_32168_8"></span>
#### 范例:修改文件的属主
$ chown 用户名:组名 文件名
@@ -157,14 +197,16 @@ Linux下提供了一组命令用于管理用户和组,比如用于创建用户
从本节开头列出的文件结构体中,可以看到仅仅有用户ID和组ID的信息,但`ls -l`的结果却显示了用户名和组名信息,这个是怎么实现的呢?下面先看看`-n`的结果:
#### 范例:查看文件的属主 ####
<span id="toc_11738_32168_9"></span>
#### 范例:查看文件的属主
$ ls -n regular_file
-rw-r--r-- 1 0 0 115 2007-12-07 23:45 regular_file
$ ls -l regular_file
-rw-r--r-- 1 root root 115 2007-12-07 23:45 regular_file
#### 范例:分析文件属主实现的背后原理 ####
<span id="toc_11738_32168_10"></span>
#### 范例:分析文件属主实现的背后原理
可以看到,`ls -n`显示了用户ID和组ID,而`ls -l`显示了它们的名字。还记得上面提到的两个配置文件/etc/passwd和/etc/group文件么?它们分别存放了用户ID和用户名,组ID和组名的对应关系,因此很容易想到`ls -l`命令在实现时是如何通过文件结构体的ID信息找到它们对应的名字信息的。如果想对`ls -l`命令的实现有更进一步的了解,可以用strace跟踪看看它是否读取了这两个配置文件。
@@ -177,11 +219,13 @@ Linux下提供了一组命令用于管理用户和组,比如用于创建用户
实际上,把属主和权限分开介绍不太好,因为只有它们两者结合才使得多用户系统成为可能,否则无法隔离不同用户对某个文件的操作,所以下面来介绍文件操作权限。
<span id="toc_11738_32168_11"></span>
### 文件权限
`ls -l`命令的结果的第一列的后9个字符中,可以看到类似这样的信息`rwxr-xr-x`,它们对应于文件结构体的st\_mode部分(st\_mode包含文件类型信息和文件权限信息两部分)。这类信息可以分成三部分,即`rwx``r-x``r-x`,分别对应该文件所属用户、所属组、其他组对该文件的操作权限,如果有rwx中任何一个表示可读、可写、可执行,如果为`-`表示没有这个权限。对应地,可以用八进制来表示它,比如`rwxr-xr-x`就可表示成二进制`111101101`,对应的八进制则为`755`。正因为如此,要修改文件的操作权限,也可以有多种方式来实现,它们都可通过`chmod`命令来修改。
#### 范例:给文件添加读、写、可执行权限 ####
<span id="toc_11738_32168_12"></span>
#### 范例:给文件添加读、写、可执行权限
比如,把regular\_file的文件权限修改为所有用户都可读、可写、可执行,即rwxrwxrwx,也可表示为111111111,翻译成八进制,则为777。这样就可以通过两种方式修改这个权限。
@@ -197,7 +241,8 @@ Linux下提供了一组命令用于管理用户和组,比如用于创建用户
如果设置了文件(程序或者命令)的setuid/setgid权限,那么用户将可用root身份去执行该文件,因此,这将可能带来安全隐患;如果设置了文件的只读权限,那么用户将仅仅对该文件将有可读权限,这为避免诸如`rm -rf`的“可恶”操作带来一定的庇佑。
#### 范例:授权普通用户执行root所属命令 ####
<span id="toc_11738_32168_13"></span>
#### 范例:授权普通用户执行root所属命令
默认情况下,系统是不允许普通用户执行passwd命令的,通过setuid/setgid,可以授权普通用户执行它。
@@ -206,7 +251,7 @@ Linux下提供了一组命令用于管理用户和组,比如用于创建用户
$ su #切换到root用户,给程序或者命令添加“粘着位”
$ chmod +s /usr/bin/passwd
$ ls -l /usr/bin/passwd
-rws--s--x 1 root root 36092 2007-06-19 14:59 /usr/bin/passwd
-rws--s--x 1 root root 36092 2007-06-19 14:59 /usr/bin/passwd
$ exit
$ passwd #普通用户通过执行该命令,修改自己的密码
@@ -216,7 +261,8 @@ Linux下提供了一组命令用于管理用户和组,比如用于创建用户
虽然这在一定程度上为管理提供了方便,比如上面的操作让普通用户可以修改自己的帐号,而不是要root帐号去为每个用户做这些工作。关于setuid/setgid的更多详细解释,请参考最后推荐的资料。
#### 范例:给重要文件加锁 ####
<span id="toc_11738_32168_14"></span>
#### 范例:给重要文件加锁
只读权限示例:给重要文件加锁(添加不可修改位[immutable])),以避免各种误操作带来的灾难性后果(例如:
`rm -rf`
@@ -232,11 +278,13 @@ Linux下提供了一组命令用于管理用户和组,比如用于创建用户
说明:`chattr`可以用于设置文件的特殊权限,更多用法请参考chattr的帮助。
<span id="toc_11738_32168_15"></span>
### 文件大小
文件大小对于普通文件而言就是文件内容的大小,而目录作为一个特殊的文件,它存放的内容是以目录结构体组织的各类文件信息,所以目录的大小一般都是固定的,它存放的文件个数自然也就有上限,即它的大小除以文件名的长度。设备文件的“文件大小”则对应设备的主、次设备号,而有名管道文件因为特殊的读写性质,所以大小常是0。硬链接(目录文件不能创建硬链接)实质上是原文件的一个完整的拷贝,因此,它的大小就是原文件的大小。而软链接只是一个inode,存放了一个指向原文件的指针,因此它的大小仅仅是原文件名的字节数。下面我们通过演示增加记忆。
#### 范例:查看普通文件和链接文件 ####
<span id="toc_11738_32168_16"></span>
#### 范例:查看普通文件和链接文件
原文件,链接文件文件大小的示例:
@@ -251,7 +299,8 @@ Linux下提供了一组命令用于管理用户和组,比如用于创建用户
$ echo ${#i} ${#j} #软链接存放的刚好是它们指向的原文件的文件名的字节数
12 22
#### 范例:查看设备文件 ####
<span id="toc_11738_32168_17"></span>
#### 范例:查看设备文件
设备号对应的文件大小:主、次设备号
@@ -262,7 +311,8 @@ Linux下提供了一组命令用于管理用户和组,比如用于创建用户
补充:主(major)、次(minor)设备号的作用有不同。当一个设备文件被打开时,内核会根据主设备号(major number)去查找在内核中已经以主设备号注册的驱动(可以`cat /proc/devices`查看已经注册的驱动号和主设备号的对应情况),而次设备号(minor number)则是通过内核传递给了驱动本身(参考《The Linux Primer》第十章)。因此,对于内核而言,通过主设备号就可以找到对应的驱动去识别某个设备,而对于驱动而言,为了能够更复杂地访问设备,比如访问设备的不同部分(如硬件通过分区分成不同部分,而出现hda1,hda2,hda3等),比如产生不同要求的随机数(如/dev/random和/dev/urandom等)。
#### 范例:查看目录 ####
<span id="toc_11738_32168_18"></span>
#### 范例:查看目录
目录文件的大小,为什么是这样呢?看看下面的目录结构体的大小,目录文件的Block中存放了该目录下所有文件名的入口。
@@ -278,18 +328,22 @@ Linux下提供了一组命令用于管理用户和组,比如用于创建用户
char d_name[NAME_MAX+1]; /* 文件名称 */
}
<span id="toc_11738_32168_19"></span>
### 文件访问、更新、修改时间
文件的时间属性可以记录用户对文件的操作信息,在系统管理、判断文件版本信息等情况下将为管理员提供参考。因此,在阅读文件时,建议用cat等阅读工具,不要用编辑工具vim去阅读,因为即使没有做任何修改操作,一旦执行了保存命令,将修改文件的时间戳信息。
<span id="toc_11738_32168_20"></span>
### 文件名
文件名并没有存放在文件结构体内,而是存放在它所在的目录结构体中。所以,在目录的同一级别中,文件名必须是唯一的。
<span id="toc_11738_32168_21"></span>
## 文件的基本操作
对于文件,常见的操作包括创建、删除、修改、读、写等。关于各种操作对应的“背后动作”将在下一章《Shell编程范例之文件系统操作》详细分析。
<span id="toc_11738_32168_22"></span>
### 范例:创建文件
socket文件是一类特殊的文件,可以通过C语言创建,这里不做介绍(暂时不知道是否可以用命令直接创建),其他文件将通过命令创建。
@@ -310,6 +364,7 @@ socket文件是一类特殊的文件,可以通过C语言创建,这里不做
$ ls 当前目录
<span id="toc_11738_32168_23"></span>
### 范例:删除文件
删除文件最直接的印象是这个文件再也不存在了,这同样可以通过`ls`或者`tree`命令呈现出来,就像树木被砍掉一个分支或者摘掉一片叶子一样。实际上,这些文件删除之后,并不是立即消失了,而是仅仅做了删除标记,因此,如果删除之后,没有相关的磁盘写操作把相应的磁盘空间“覆盖”,那么原理上是可以恢复的(虽然如此,但是这样的工作往往很麻烦,所以在删除一些重要数据时,请务必三思而后行,比如做好备份工作),相应的做法可以参考后续资料。
@@ -322,6 +377,7 @@ socket文件是一类特殊的文件,可以通过C语言创建,这里不做
rm有两个非常重要的参数,一个是-f,这个命令是非常“野蛮的”,它估计给很多Linux user带来了痛苦,另外一个是-i,这个命令是非常“温柔的”,它估计让很多用户感觉烦躁不已。用哪个还是根据您的“心情”吧,如果做好了充分的备份工作,或者采取了一些有效避免灾难性后果的动作的话,您在做这些工作的时候就可以放心一些啦。
<span id="toc_11738_32168_24"></span>
### 范例:复制文件
文件的复制通常是指文件内容的“临时”复制。通过这一节开头的介绍,我们应该了解到,文件的硬链接和软链接在某种意义上说也是“文件的复制”,前者同步复制文件内容,后者在读写的情况下同步“复制”文件内容。例如:
@@ -339,12 +395,14 @@ rm有两个非常重要的参数,一个是-f,这个命令是非常“野蛮
$ ln -s regular_file regluar_file_soft_link
<span id="toc_11738_32168_25"></span>
### 范例:修改文件名
修改文件名实际上仅仅修改了文件名标识符。可以通过mv命令来实现修改文件名操作(即重命名)。
$ mv regular_file regular_file_new_name
<span id="toc_11738_32168_26"></span>
### 范例:编辑文件
编辑文件实际上是操作文件的内容,对应普通文本文件的编辑,这里主要涉及到文件内容的读、写、追加、删除等。这些工作通常会通过专门的编辑器来做,这类编辑器有命令行下的vim、emacs和图形界面下的gedit,kedit等。如果是一些特定的文件,会有专门的编辑和处理工具,比如图像处理软件gimp,文档编辑软件OpenOffice等。这些工具一般都会有专门的教程。
@@ -372,11 +430,12 @@ rm有两个非常重要的参数,一个是-f,这个命令是非常“野蛮
$ cat test_redirect_file
abcde
<span id="toc_11738_32168_27"></span>
### 范例:压缩/解压缩文件
压缩和解压缩文件在一定意义上来说是为了方便文件内容的传输,不过也可能有一些特定的用途,比如内核和文件系统的映像文件等(更多相关的知识请参考后续资料)。
这里仅介绍几种常见的压缩和解压缩方法:
这里仅介绍几种常见的压缩和解压缩方法:
tar
@@ -405,6 +464,7 @@ tar.bz2
通过上面的演示,应该已经非常清楚tar/bzip2/bunzip2,gzip/gunzip命令的角色了吧?如果还不清楚,多操作和比较一些上面的命令,并查看它们的手册:`man tar`...
<span id="toc_11738_32168_28"></span>
### 范例:文件搜索(文件定位)
文件搜索是指在某个目录层次中找出具有某些属性的文件在文件系统中的位置,这个位置如果扩展到整个网络,那么可以表示为一个URL地址,对于本地的地址,可以表示为file://+本地路径。本地路径在Linux系统下是以/开头,例如,每个用户的家目录可以表示为:file:///home/。下面仅仅介绍本地文件搜索的一些办法。
@@ -460,6 +520,7 @@ updatedb+locate基本使用演示
值得强调的是,这些命令对文件的操作非常有意义。它们在某个程度上把文件系统结构给抽象了,使得对整个文件系统的操作简化为对单个文件的操作,而单个文件如果仅仅考虑文本部分,那么最终却转化成了之前的字符串操作,即上一节讨论过的内容。为了更清楚地了解文件的组织结构,文件之间的关系,在下一节将深入探讨文件系统。
<span id="toc_11738_32168_29"></span>
## 参考资料
- [从文件 I/O 看 Linux
@@ -489,6 +550,7 @@ updatedb+locate基本使用演示
- [Linux压缩/解压缩方式大全](http://www.cnblogs.com/eoiioe/archive/2008/09/20/1294681.html)
- [Everything is a byte](http://www.reteam.org/papers/e56.pdf)
<span id="toc_11738_32168_30"></span>
## 后记
- 考虑到文件和文件系统的重要性,将把它分成三个小节来介绍:文件、文件系统、程序与进程。在“文件”这一部分,主要介绍文件的基本属性和常规操作,在“文件系统”那部分,将深入探讨Linux文件系统的各个部分(包括Linux文件系统的结构、具体某个文件系统的大体结构分析、底层驱动的工作原理),在“程序与进程”一节将专门讨论可执行文件的相关内容(包括不同的程序类型、加载执行过程、不同进程之间的交互[命令管道和无名管道、信号通信]、对进程的控制等)
+58 -4
View File
@@ -1,9 +1,39 @@
# 文件系统操作
- [前言](#toc_23937_24032_1)
- [文件系统在Linux操作系统中的位置](#toc_23937_24032_2)
- [硬件管理和设备驱动](#toc_23937_24032_3)
- [范例:查找设备所需的驱动文件](#toc_23937_24032_4)
- [范例:查看已经加载的设备驱动](#toc_23937_24032_5)
- [范例:卸载设备驱动](#toc_23937_24032_6)
- [范例:挂载设备驱动](#toc_23937_24032_7)
- [范例:查看设备驱动对应的设备文件](#toc_23937_24032_8)
- [范例:访问设备文件](#toc_23937_24032_9)
- [理解、查看磁盘分区](#toc_23937_24032_10)
- [磁盘分区基本原理](#toc_23937_24032_11)
- [通过分析MBR来理解分区原理](#toc_23937_24032_12)
- [分区和文件系统的关系](#toc_23937_24032_13)
- [常见分区类型](#toc_23937_24032_14)
- [范例:格式化文件系统](#toc_23937_24032_15)
- [分区、逻辑卷和文件系统的关系](#toc_23937_24032_16)
- [文件系统的可视化结构](#toc_23937_24032_17)
- [范例:挂载文件系统](#toc_23937_24032_18)
- [范例:卸载某个分区](#toc_23937_24032_19)
- [如何制作一个文件系统](#toc_23937_24032_20)
- [范例:用dd创建一个固定大小的文件](#toc_23937_24032_21)
- [范例:用mkfs格式化文件](#toc_23937_24032_22)
- [范例:挂载刚创建的文件系统](#toc_23937_24032_23)
- [范例:对文件系统进行读、写、删除等操作](#toc_23937_24032_24)
- [如何开发自己的文件系统](#toc_23937_24032_25)
- [后记](#toc_23937_24032_26)
<span id="toc_23937_24032_1"></span>
## 前言
准备了很久,找了好多天资料,还不知道应该如何动笔写:因为担心拿捏不住,所以一方面继续查找资料,一方面思考如何来写。作为《Shell编程范例序列》的一部分,希望它能够很好地帮助Shell程序员理解如何用Shell命令来完成和Linux系统关系非常大的文件系统的各种操作,希望让Shell程序员中对文件系统"混沌"的状态从此消失,希望文件系统以一种更为清晰的样子呈现在眼前。
<span id="toc_23937_24032_2"></span>
## 文件系统在Linux操作系统中的位置
如何来认识文件系统呢?从Shell程序员的角度来看,文件系统就是一个用来组织各种文件的方法。但是文件系统无法独立于硬件存储设备和操作系统而存在,因此还是有必要来弄清楚硬件存储设备、分区、操作系统、逻辑卷、文件系统等各种概念之间的联系,以便理解文件系统常规操作的一些“细节”。这个联系或许(也许会有一些问题)可以通过这样一种方式来呈现:
@@ -21,10 +51,12 @@
- [第九章 文件系统](http://man.chinaunix.net/tech/lyceum/linuxK/fs/filesystem.html)
- [Linux逻辑盘卷管理LVM详解](http://unix-cd.com/vc/www/28/2007-06/1178.html)
<span id="toc_23937_24032_3"></span>
## 硬件管理和设备驱动
Linux系统通过设备驱动管理硬件设备。如果添加了新的硬件设备,那么需要编写相应的硬件驱动来管理它。对于一些常见的硬件设备,系统已经自带了相应的驱动,编译内核时,选中它们,然后编译成内核的一部分或者以模块的方式编译。如果以模块的方式编译,那么可以在系统的/lib/modules/\`uname -r\`目录下找到对应的模块文件。
<span id="toc_23937_24032_4"></span>
### 范例:查找设备所需的驱动文件
比如,可以这样找到相应的scsi驱动和usb驱动对应的模块文件:
@@ -51,16 +83,18 @@ Linux系统通过设备驱动管理硬件设备。如果添加了新的硬件设
$ lsmod
<span id="toc_23937_24032_5"></span>
### 范例:查看已经加载的设备驱动
查看scsi和usb相关驱动,结果各列为模块名、模块大小、被其他模块的引用情况(引用次数、引用它们的模块)
$ lsmod | egrep "scsi|usb"
usbhid 29536 0
usbhid 29536 0
hid 28928 1 usbhid
usbcore 138632 4 usbhid,ehci_hcd,ohci_hcd
scsi_mod 147084 4 sg,sr_mod,sd_mod,libata
<span id="toc_23937_24032_6"></span>
### 范例:卸载设备驱动
下面卸载usbhid模块看看(不要卸载scsi的驱动!因为你的系统可能就跑在上面,如果确实想玩玩,卸载前记得保存数据),通过rmmod命令就可以实现,先切换到root用户:
@@ -72,6 +106,7 @@ Linux系统通过设备驱动管理硬件设备。如果添加了新的硬件设
$ lsmod | grep ^usbhid
<span id="toc_23937_24032_7"></span>
### 范例:挂载设备驱动
如果有个usb鼠标,那么移动一下,是不是发现动不了啦?因为设备驱动都没有了,设备自然就没法用罗。不过不要紧张,既然知道原因,那么重新加载驱动就可以,下面用`insmod`把usbhid模块重新加载上。
@@ -87,6 +122,7 @@ Linux系统通过设备驱动管理硬件设备。如果添加了新的硬件设
到这里,硬件设备和设备驱动之间关系应该是比较清楚了。如果没有,那么继续下面的内容。
<span id="toc_23937_24032_8"></span>
### 范例:查看设备驱动对应的设备文件
Linux设备驱动关联着相应的设备文件,而设备文件则和硬件设备一一对应。这些设备文件都统一存放在系统的/dev/目录下。
@@ -114,6 +150,7 @@ Linux设备驱动关联着相应的设备文件,而设备文件则和硬件设
因为设备文件和硬件是对应的,这样可以直接从/dev/sda(如果是IDE的硬盘,那么对应的设备就是/dev/hda啦)设备中读出硬盘的信息,例如:
<span id="toc_23937_24032_9"></span>
### 范例:访问设备文件
用dd命令复制出硬盘的前512个字节,要root用户
@@ -150,12 +187,14 @@ bs是块的大小(以字节bytes为单位),count是块数
- [The Linux Kernel Module Programming Guide](http://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.html)
- [Linux设备驱动开发](http://lwn.net/Kernel/LDD3/)
<span id="toc_23937_24032_10"></span>
## 理解、查看磁盘分区
实际上内存、u盘等都可以作为文件系统底层的“存储”设备,但是这里仅用硬盘作为实例来介绍磁盘和分区的关系。
目前Linux的分区依然采用第一台PC硬盘所使用的分区原理,下面逐步分析和演示这一分区原理。
<span id="toc_23937_24032_11"></span>
### 磁盘分区基本原理
先来看看几个概念:
@@ -178,6 +217,7 @@ Linux系统中每一个硬盘设备最多由4个主分区(包括扩展分区
它规定了这个分区上的文件系统的类型。Linux支持诸如msdoc,vfat,ext2,ext3等诸多的文件系统类型,更多信息在下一小节进行进一步的介绍。
<span id="toc_23937_24032_12"></span>
### 通过分析MBR来理解分区原理
下面通过分析硬盘的前512个字节(即MBR)来分析和理解分区。
@@ -195,7 +235,7 @@ Linux系统中每一个硬盘设备最多由4个主分区(包括扩展分区
下面用file,od,fdisk等命令来分析这段MBR的数据,并对照上图以便加深理解。
$ file mbr.bin
$ file mbr.bin
mbr.bin: x86 boot sector, LInux i386 boot LOader; partition 3: ID=0x82, starthead 254, startsector 19535040, 1959930 sectors; partition 4: ID=0x5, starthead 254, startsector 21494970, 56661255 sectors, code offset 0x48
$ od -x mbr.bin | tail -6 #仅关注中间的64字节,所以截取了结果中后6行
0000660 0000 0000 0000 0000 a666 a666 0000 0180
@@ -242,12 +282,14 @@ file命令的结果显示,刚拷贝的512字节是启动扇区,用分号分
- [Redhat9磁盘分区简介](http://blog.csdn.net/fowse/article/details/7220021)
- [Linux partition HOWTO](http://www.tldp.org/HOWTO/Partition/)
<span id="toc_23937_24032_13"></span>
## 分区和文件系统的关系
在没有引入逻辑卷之前,分区类型和文件系统类型几乎可以同等对待,设置分区类型的过程就是格式化分区,建立相应的文件系统类型的过程。
下面主要介绍如何建立分区和文件系统类型的联系,即如何格式化分区为指定的文件系统类型。
<span id="toc_23937_24032_14"></span>
### 常见分区类型
先来看看Linux下文件系统的常见类型(如果要查看所有Linux支持的文件类型,可以用fdisk命令的l命令查看,或者通过`man fs`查看,也可通过/proc/filesystems查看到当前内核支持的文件系统类型)
@@ -262,6 +304,7 @@ file命令的结果显示,刚拷贝的512字节是启动扇区,用分号分
格式化的过程实际上就是重新组织分区的过程,可通过`mkfs`命令来实现,当然也可以通过`fdisk`等命令来实现。这里仅介绍mkfs,mkfs可用来对一个已有的分区进行格式化,不能实现分区操作(如果要对一个磁盘进行分区和格式化,那么可以用fdisk)。格式化后,相应分区上的数据就会通过某种特别的文件系统类型进行组织。
<span id="toc_23937_24032_15"></span>
### 范例:格式化文件系统
例如:把/dev/sda9分区格式化为ext3的文件系统。
@@ -282,10 +325,12 @@ file命令的结果显示,刚拷贝的512字节是启动扇区,用分号分
[2](http://www.ibm.com/developerworks/cn/linux/l-initrd.html)
- [高级文件系统实现者指南](http://www.ibm.com/Search/?q=%E9%AB%98%E7%BA%A7%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%AE%9E%E7%8E%B0%E8%80%85%E6%8C%87%E5%8D%97&v=17&en=utf&lang=en&cc=us)
<span id="toc_23937_24032_16"></span>
## 分区、逻辑卷和文件系统的关系
上一节直接把分区格式化为某种文件系统类型,但是考虑到扩展新的存储设备的需要,开发人员在文件系统和分区之间引入了逻辑卷。考虑到时间关系,这里不再详述,请参考资料:[Linux逻辑卷管理详解](http://unix-cd.com/vc/www/28/2007-06/1178.html)
<span id="toc_23937_24032_17"></span>
## 文件系统的可视化结构
文件系统最终呈现出来的是一种可视化的结构,可用ls,find,tree等命令把它呈现出来。它就像一颗倒挂的“树”,在树的节点上还可以挂载新的“树”。
@@ -294,6 +339,7 @@ file命令的结果显示,刚拷贝的512字节是启动扇区,用分号分
一个文件系统可以通过一个设备挂载(mount)到某个目录下,这个目录被称为挂载点。有趣的是,在Linux下,一个目录本身还可以挂载到另外一个目录下,一个格式化了的文件也可以通过一个特殊的设备/dev/loop进行挂载(如iso文件)。另外,就文件系统而言,Linux不仅支持本地文件系统,还支持远程文件系统(如nfs)。
<span id="toc_23937_24032_18"></span>
### 范例:挂载文件系统
下面简单介绍文件系统挂载的几个实例。
@@ -362,6 +408,7 @@ proc`
这个非常有意思,比如可以把某个目录挂载到ftp服务的根目录下,而无须把内容复制过去,就可以把相应目录中的资源提供给别人共享。
<span id="toc_23937_24032_19"></span>
### 范例:卸载某个分区
以上都只提到了挂载,那怎么卸载呢?用umount命令跟上挂载的源地址或者挂载点(设备,文件,远程目录等)就可以。例如:
@@ -380,10 +427,12 @@ proc`
- [从文件I/O看linux的虚拟文件系统](http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/)
- [用Graphviz进行可视化操作──绘制函数调用关系图](http://blog.csdn.net/unbutun/article/details/2838560)
<span id="toc_23937_24032_20"></span>
## 如何制作一个文件系统
Linux文件系统下有一些最基本的目录,不同的目录下存放着不同作用的各类文件。最基本的目录有/etc, /lib, /dev, /bin等,它们分别存放着系统配置文件,库文件,设备文件和可执行程序。这些目录一般情况下是必须的,在做嵌入式开发时,需要手动或者是用busybox等工具来创建这样一个基本的文件系统。这里仅制作一个非常简单的文件系统,并对该文件系统进行各种常规操作,以便加深对文件系统的理解。
<span id="toc_23937_24032_21"></span>
### 范例:用dd创建一个固定大小的文件
还记得dd命令么?就用它来产生一个固定大小的文件,这个为1M(1024\*1024 bytes)的文件
@@ -392,13 +441,14 @@ Linux文件系统下有一些最基本的目录,不同的目录下存放着不
查看文件类型,这里的minifs是一个充满\\0的文件,没有任何特定的数据结构
$ file minifs
$ file minifs
minifs: data
说明:/dev/zero是一个非常特殊的设备,如果读取它,可以获取任意多个\\0。
接着把该文件格式化为某个指定文件类型的文件系统。(是不是觉得不可思议,文件也可以格式化?是的,不光是设备可以,文件也可以以某种文件系统类型进行组织,但是需要注意的是,某些文件系统(如ext3)要求被格式化的目标最少有64M的空间)。
<span id="toc_23937_24032_22"></span>
### 范例:用mkfs格式化文件
$ mkfs.ext2 minifs
@@ -408,6 +458,7 @@ Linux文件系统下有一些最基本的目录,不同的目录下存放着不
$ file minifs
minifs: Linux rev 1.0 ext2 filesystem data
<span id="toc_23937_24032_23"></span>
### 范例:挂载刚创建的文件系统
因为该文件以文件系统的类型组织了,那么可以用mount命令挂载并使用它。
@@ -422,6 +473,7 @@ Linux文件系统下有一些最基本的目录,不同的目录下存放着不
$ ls /mnt/
lost+found
<span id="toc_23937_24032_24"></span>
### 范例:对文件系统进行读、写、删除等操作
在该文件系统下进行各种常规操作,包括读、写、删除等。(每次操作前先把minifs文件保存一份,以便比较,结合相关资料就可以深入地分析各种操作对文件系统的改变情况,从而深入理解文件系统作为一种组织数据的方式的实现原理等)
@@ -437,7 +489,7 @@ Linux文件系统下有一些最基本的目录,不同的目录下存放着不
创建一个文件后,比较此时文件系统和之前文件系统的异同
$ diff orig.od touch.od
diff orig.od touch.od
diff orig.od touch.od
61,63c61,64
< 0060020 000c 0202 2e2e 0000 000b 0000 03e8 020a
< 0060040 6f6c 7473 662b 756f 646e 0000 0000 0000
@@ -492,10 +544,12 @@ cache吧,这里就是把cache中的数据写到磁盘中)
- [Build a mini filesystem in linux with BusyBox](http://dslab.lzu.edu.cn:8080/docs/summer_school_2007/team3/doc/build_a_mini_filesystem_with_busybox)
- [ext2 文件系统](http://man.chinaunix.net/tech/lyceum/linuxK/fs/filesystem.html)
<span id="toc_23937_24032_25"></span>
## 如何开发自己的文件系统
随着fuse的出现,在用户空间开发文件系统成为可能,如果想开发自己的文件系统,那么推荐阅读:[使用fuse开发自己的文件系统](http://www.ibm.com/developerworks/cn/linux/l-fuse/)。
<span id="toc_23937_24032_26"></span>
## 后记
- 2007年12月22日,收集了很多资料,写了整体的框架
+92 -34
View File
@@ -1,5 +1,36 @@
# 进程操作
- [前言](#toc_19293_24090_1)
- [什么是程序,什么又是进程](#toc_19293_24090_2)
- [进程的创建](#toc_19293_24090_3)
- [范例:让程序在后台运行](#toc_19293_24090_4)
- [范例:查看进程ID](#toc_19293_24090_5)
- [范例:查看进程的内存映像](#toc_19293_24090_6)
- [查看进程的属性和状态](#toc_19293_24090_7)
- [范例:通过ps命令查看进程属性](#toc_19293_24090_8)
- [范例:通过pstree查看进程亲缘关系](#toc_19293_24090_9)
- [范例:用top动态查看进程信息](#toc_19293_24090_10)
- [范例:确保特定程序只有一个副本在运行](#toc_19293_24090_11)
- [调整进程的优先级](#toc_19293_24090_12)
- [范例:获取进程优先级](#toc_19293_24090_13)
- [范例:调整进程的优先级](#toc_19293_24090_14)
- [结束进程](#toc_19293_24090_15)
- [范例:结束进程](#toc_19293_24090_16)
- [范例:暂停某个进程](#toc_19293_24090_17)
- [范例:查看进程退出状态](#toc_19293_24090_18)
- [进程通信](#toc_19293_24090_19)
- [范例:无名管道(pipe)](#toc_19293_24090_20)
- [范例:有名管道(named pipe)](#toc_19293_24090_21)
- [范例:信号(Signal)](#toc_19293_24090_22)
- [作业和作业控制](#toc_19293_24090_23)
- [范例:创建后台进程,获取进程的作业号和进程号](#toc_19293_24090_24)
- [范例:把作业调到前台并暂停](#toc_19293_24090_25)
- [范例:查看当前作业情况](#toc_19293_24090_26)
- [范例:启动停止的进程并运行在后台](#toc_19293_24090_27)
- [参考资料](#toc_19293_24090_28)
<span id="toc_19293_24090_1"></span>
## 前言
本来本章关注的内容比较多,包括程序开发过程的细节、ELF格式的分析、进程的内存映像等,后来搞得“雪球越滚越大”,甚至脱离了Shell编程关注的内容。
@@ -21,6 +52,7 @@
说明:上面的整个序列都已经编写完成,在网络中都可以搜索到,作者计划在近期整理成册,陆续更新到[TinyLab.org](tinylab.org)。
<span id="toc_19293_24090_2"></span>
## 什么是程序,什么又是进程
程序是指令的集合,而进程则是程序执行的基本单元。为了让程序完成它的工作,必须让程序运行起来成为进程,进而利用处理器资源、内存资源,进行各种I/O操作,从而完成某项特定工作。
@@ -35,15 +67,18 @@
下面简单讨论Linux下面如何通过Shell进行进程的相关操作。
<span id="toc_19293_24090_3"></span>
## 进程的创建
通常在命令行键入某个程序文件名以后,一个进程就被创建了。例如,
<span id="toc_19293_24090_4"></span>
### 范例:让程序在后台运行
$ sleep 100 &
[1] 9298
<span id="toc_19293_24090_5"></span>
### 范例:查看进程ID
`pidof`可以查看指定程序名的进程ID
@@ -51,13 +86,14 @@
$ pidof sleep
9298
<span id="toc_19293_24090_6"></span>
### 范例:查看进程的内存映像
$ cat /proc/9298/maps
08048000-0804b000 r-xp 00000000 08:01 977399 /bin/sleep
0804b000-0804c000 rw-p 00003000 08:01 977399 /bin/sleep
0804c000-0806d000 rw-p 0804c000 00:00 0 [heap]
b7c8b000-b7cca000 r--p 00000000 08:01 443354
b7c8b000-b7cca000 r--p 00000000 08:01 443354
...
bfbd8000-bfbed000 rw-p bfbd8000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
@@ -70,10 +106,12 @@
需要补充一点的是:在命令行下执行程序,可以通过`ulimit`内置命令来设置进程可以利用的资源,比如进程可以打开的最大文件描述符个数,最大的栈空间,虚拟内存空间等。具体用法见`help ulimit`
<span id="toc_19293_24090_7"></span>
## 查看进程的属性和状态
可以通过`ps`命令查看进程相关属性和状态,这些信息包括进程所属用户,进程对应的程序,进程对cpu和内存的使用情况等信息。熟悉如何查看它们有助于进行相关的统计分析等操作。
<span id="toc_19293_24090_8"></span>
### 范例:通过ps命令查看进程属性
查看系统当前所有进程的属性:
@@ -111,6 +149,7 @@
76332 gedit
58920 scim-panel-gtk
<span id="toc_19293_24090_9"></span>
### 范例:通过pstree查看进程亲缘关系
系统所有进程之间都有“亲缘”关系,可以通过`pstree`查看这种关系:
@@ -119,6 +158,7 @@
上面会打印系统进程调用树,可以非常清楚地看到当前系统中所有活动进程之间的调用关系。
<span id="toc_19293_24090_10"></span>
### 范例:用top动态查看进程信息
$ top
@@ -129,6 +169,7 @@
$ top -n 1 -b
<span id="toc_19293_24090_11"></span>
### 范例:确保特定程序只有一个副本在运行
下面来讨论一个有趣的问题:如何让一个程序在同一时间只有一个在运行。
@@ -146,7 +187,7 @@
每次运行时先在指定位置检查是否存在一个保存自己进程ID的文件,如果不存在,那么继续执行,如果存在,那么查看该进程ID是否正在运行,如果在,那么退出,否则往该文件重新写入新的进程ID,并继续。
pidfile=/tmp/$0".pid"
pidfile=/tmp/$0".pid"
if [ -f $pidfile ]; then
OLDPID=$(cat $pidfile)
ps -e -o "%p" | tr -d " " | grep -q "^$OLDPID$"
@@ -162,15 +203,18 @@
更多实现策略自己尽情发挥吧!
<span id="toc_19293_24090_12"></span>
## 调整进程的优先级
在保证每个进程都能够顺利执行外,为了让某些任务优先完成,那么系统在进行进程调度时就会采用一定的调度办法,比如常见的有按照优先级的时间片轮转的调度算法。这种情况下,可以通过`renice`调整正在运行的程序的优先级,例如:
在保证每个进程都能够顺利执行外,为了让某些任务优先完成,那么系统在进行进程调度时就会采用一定的调度办法,比如常见的有按照优先级的时间片轮转的调度算法。这种情况下,可以通过`renice`调整正在运行的程序的优先级,例如:
<span id="toc_19293_24090_13"></span>
### 范例:获取进程优先级
$ ps -e -o "%p %c %n" | grep xfs
5089 xfs 0
<span id="toc_19293_24090_14"></span>
### 范例:调整进程的优先级
$ renice 1 -p 5089
@@ -181,10 +225,12 @@
$ ps -e -o "%p %c %n" | grep xfs #再看看,优先级已经被调整过来了
5089 xfs 1
<span id="toc_19293_24090_15"></span>
## 结束进程
既然可以通过命令行执行程序,创建进程,那么也有办法结束它。可以通过`kill`命令给用户自己启动的进程发送某个信号让进程终止,当然“万能”的root几乎可以`kill`所有进程(除了`init`之外)。例如,
<span id="toc_19293_24090_16"></span>
### 范例:结束进程
$ sleep 50 & #启动一个进程
@@ -212,6 +258,7 @@
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
<span id="toc_19293_24090_17"></span>
### 范例:暂停某个进程
例如,用`kill`命令发送`SIGSTOP`信号给某个程序,让它暂停,然后发送`SIGCONT`信号让它继续运行。
@@ -232,6 +279,7 @@
可见`kill`命令提供了非常好的功能,不过它只能根据进程的ID或者作业来控制进程,而`pkill``killall`提供了更多选择,它们扩展了通过程序名甚至是进程的用户名来控制进程的方法。更多用法请参考它们的手册。
<span id="toc_19293_24090_18"></span>
### 范例:查看进程退出状态
当程序退出后,如何判断这个程序是正常退出还是异常退出呢?还记得Linux下,那个经典"hello,world"程序吗?在代码的最后总是有条`return 0`语句。这个`return 0`实际上是让程序员来检查进程是否正常退出的。如果进程返回了一个其他的数值,那么可以肯定地说这个进程异常退出了,因为它都没有执行到`return 0`这条语句就退出了。
@@ -254,10 +302,12 @@
貌似返回0成为了一个潜规则,虽然没有标准明确规定,不过当程序正常返回时,总是可以从`$?`中检测到0,但是异常时,总是检测到一个非0值。这就告诉我们在程序的最后最好是跟上一个`exit 0`以便任何人都可以通过检测`$?`确定程序是否正常结束。如果有一天,有人偶尔用到你的程序,试图检查它的退出状态,而你却在程序的末尾莫名地返回了一个`-1`或者`1`,那么他将会很苦恼,会怀疑他自己编写的程序到底哪个地方出了问题,检查半天却不知所措,因为他太信任你了,竟然从头至尾都没有怀疑你的编程习惯可能会与众不同!
<span id="toc_19293_24090_19"></span>
## 进程通信
为便于设计和实现,通常一个大型的任务都被划分成较小的模块。不同模块之间启动后成为进程,它们之间如何通信以便交互数据,协同工作呢?在《UNIX环境高级编程》一书中提到很多方法,诸如管道(无名管道和有名管道)、信号(signal)、报文(Message)队列(消息队列)、共享内存(mmap/munmap)、信号量(semaphore,主要是同步用,进程之间,进程的不同线程之间)、套接口(Socket,支持不同机器之间的进程通信)等,而在Shell中,通常直接用到的就有管道和信号等。下面主要介绍管道和信号机制在Shell编程时的一些用法。
<span id="toc_19293_24090_20"></span>
### 范例:无名管道(pipe)
在Linux下,可以通过`|`连接两个程序,这样就可以用它来连接后一个程序的输入和前一个程序的输出,因此被形象地叫做个管道。在C语言中,创建无名管道非常简单方便,用`pipe`函数,传入一个具有两个元素的`int`型的数组就可以。这个数组实际上保存的是两个文件描述符,父进程往第一个文件描述符里头写入东西后,子进程可以从第一个文件描述符中读出来。
@@ -268,6 +318,7 @@
也许会觉得这个“管子”好有魔法,竟然真地能够链接两个程序的输入和输出,它们到底是怎么实现的呢?实际上当输入这样一组命令时,当前Shell会进行适当的解析,把前面一个进程的输出关联到管道的输出文件描述符,把后面一个进程的输入关联到管道的输入文件描述符,这个关联过程通过输入输出重定向函数`dup`(或者`fcntl`)来实现。
<span id="toc_19293_24090_21"></span>
### 范例:有名管道(named pipe)
有名管道实际上是一个文件(无名管道也像一个文件,虽然关系到两个文件描述符,不过只能一边读另外一边写),不过这个文件比较特别,操作时要满足先进先出,而且,如果试图读一个没有内容的有名管道,那么就会被阻塞,同样地,如果试图往一个有名管道里写东西,而当前没有程序试图读它,也会被阻塞。下面看看效果。
@@ -284,45 +335,45 @@
- 应用程序的代码
$ cat app.sh
#!/bin/bash
$ cat app.sh
#!/bin/bash
FIFO=fifo_test
while :;
do
CI=`cat $FIFO` #CI --> Control Info
case $CI in
0) echo "The CONTROL number is ZERO, do something ..."
;;
1) echo "The CONTROL number is ONE, do something ..."
;;
*) echo "The CONTROL number not recognized, do something else..."
;;
esac
done
FIFO=fifo_test
while :;
do
CI=`cat $FIFO` #CI --> Control Info
case $CI in
0) echo "The CONTROL number is ZERO, do something ..."
;;
1) echo "The CONTROL number is ONE, do something ..."
;;
*) echo "The CONTROL number not recognized, do something else..."
;;
esac
done
- 控制程序的代码
$ cat control.sh
#!/bin/bash
$ cat control.sh
#!/bin/bash
FIFO=fifo_test
CI=$1
FIFO=fifo_test
CI=$1
[ -z "$CI" ] && echo "the control info should not be empty" && exit
[ -z "$CI" ] && echo "the control info should not be empty" && exit
echo $CI > $FIFO
echo $CI > $FIFO
- 一个程序通过管道控制另外一个程序的工作
$ chmod +x app.sh control.sh #修改这两个程序的可执行权限,以便用户可以执行它们
$ ./app.sh #在一个终端启动这个应用程序,在通过./control.sh发送控制码以后查看输出
The CONTROL number is ONE, do something ... #发送1以后
The CONTROL number is ZERO, do something ... #发送0以后
The CONTROL number not recognized, do something else... #发送一个未知的控制码以后
$ ./control.sh 1 #在另外一个终端,发送控制信息,控制应用程序的工作
$ ./control.sh 0
$ ./control.sh 4343
$ chmod +x app.sh control.sh #修改这两个程序的可执行权限,以便用户可以执行它们
$ ./app.sh #在一个终端启动这个应用程序,在通过./control.sh发送控制码以后查看输出
The CONTROL number is ONE, do something ... #发送1以后
The CONTROL number is ZERO, do something ... #发送0以后
The CONTROL number not recognized, do something else... #发送一个未知的控制码以后
$ ./control.sh 1 #在另外一个终端,发送控制信息,控制应用程序的工作
$ ./control.sh 0
$ ./control.sh 4343
这样一种应用架构非常适合本地的多程序任务设计,如果结合`web cgi`,那么也将适合远程控制的要求。引入`web cgi`的唯一改变是,要把控制程序`./control.sh`放到web的cgi目录下,并对它作一些修改,以使它符合CGI的规范,这些规范包括文档输出格式的表示(在文件开头需要输出`content-tpye: text/html`以及一个空白行)和输入参数的获取(web输入参数都存放在`QUERY_STRING`环境变量里头)。因此一个非常简单的CGI控制程序可以写成这样:
@@ -331,7 +382,7 @@
FIFO=./fifo_test
CI=$QUERY_STRING
[ -z "$CI" ] && echo "the control info should not be empty" && exit
[ -z "$CI" ] && echo "the control info should not be empty" && exit
echo -e "content-type: text/html\n\n"
echo $CI > $FIFO
@@ -346,6 +397,7 @@
在C语言中,如果要使用有名管道,和Shell类似,只不过在读写数据时用`read, write`调用,在创建`fifo`时用`mkfifo`函数调用。
<span id="toc_19293_24090_22"></span>
### 范例:信号(Signal)
信号是软件中断,Linux用户可以通过`kill`命令给某个进程发送一个特定的信号,也可以通过键盘发送一些信号,比如`CTRL+C`可能触发`SGIINT`信号,而`CTRL+\`可能触发`SGIQUIT`信号等,除此之外,内核在某些情况下也会给进程发送信号,比如在访问内存越界时产生`SGISEGV`信号,当然,进程本身也可以通过`kill``raise`等函数给自己发送信号。对于Linux下支持的信号类型,大家可以通过`man 7 signal`或者`kill -l`查看到相关列表和说明。
@@ -369,15 +421,18 @@
$ ./sigexit.sh #实际Shell编程会用该方式在程序退出时来做一些清理临时文件的收尾工作
hello, world
<span id="toc_19293_24090_23"></span>
## 作业和作业控制
当我们为完成一些复杂的任务而将多个命令通过`|,\>,<, ;, (,)`等组合在一起时,通常这个命令序列会启动多个进程,它们间通过管道等进行通信。而有时在执行一个任务的同时,还有其他的任务需要处理,那么就经常会在命令序列的最后加上一个&,或者在执行命令后,按下`CTRL+Z`让前一个命令暂停。以便做其他的任务。等做完其他一些任务以后,再通过`fg`命令把后台任务切换到前台。这样一种控制过程通常被成为作业控制,而那些命令序列则被成为作业,这个作业可能涉及一个或者多个程序,一个或者多个进程。下面演示一下几个常用的作业控制操作。
<span id="toc_19293_24090_24"></span>
### 范例:创建后台进程,获取进程的作业号和进程号
$ sleep 50 &
[1] 11137
<span id="toc_19293_24090_25"></span>
### 范例:把作业调到前台并暂停
使用Shell内置命令`fg`把作业1调到前台运行,然后按下`CTRL+Z`让该进程暂停
@@ -387,16 +442,18 @@
^Z
[1]+ Stopped sleep 50
<span id="toc_19293_24090_26"></span>
### 范例:查看当前作业情况
$ jobs #查看当前作业情况,有一个作业停止
[1]+ Stopped sleep 50
$ sleep 100 & #让另外一个作业在后台运行
[2] 11138
[2] 11138
$ jobs #查看当前作业情况,一个正在运行,一个停止
[1]+ Stopped sleep 50
[2]- Running sleep 100 &
<span id="toc_19293_24090_27"></span>
### 范例:启动停止的进程并运行在后台
$ bg %1
@@ -404,6 +461,7 @@
不过,要在命令行下使用作业控制,需要当前Shell,内核终端驱动等对作业控制支持才行。
<span id="toc_19293_24090_28"></span>
## 参考资料
- 《UNIX环境高级编程》
+32
View File
@@ -1,13 +1,33 @@
# 网络操作
- [前言](#toc_5782_1744_1)
- [网络原理介绍](#toc_5782_1744_2)
- [我们的网络世界](#toc_5782_1744_3)
- [网络体系结构和网络协议介绍](#toc_5782_1744_4)
- [Linux下网络“实战”](#toc_5782_1744_5)
- [如何把我们的Linux主机接入网络](#toc_5782_1744_6)
- [范例:通过dhclient获取IP地址](#toc_5782_1744_7)
- [范例:静态配置IP地址](#toc_5782_1744_8)
- [用Linux搭建网桥](#toc_5782_1744_9)
- [用Linux做路由](#toc_5782_1744_10)
- [用Linux搭建各种常规的网络服务](#toc_5782_1744_11)
- [Linux下网络问题诊断与维护](#toc_5782_1744_12)
- [Linux下网络编程与开发](#toc_5782_1744_13)
- [后记](#toc_5782_1744_14)
- [参考资料](#toc_5782_1744_15)
<span id="toc_5782_1744_1"></span>
## 前言
前面章节已经介绍了Shell编程范例之数值、布尔值、字符串、文件、文件系统、进程等的操作。这些内容基本覆盖了网络中某个独立机器正常工作的“方方面面”,现在需要把视角从单一的机器延伸到这些机器通过各种网络设备和协议连接起来的网络世界,分析网络拓扑结构、网络工作原理、了解各种常见网络协议、各种常见硬件工作原理、网络通信与安全相关软件以及工作原理分析等。
不过,因为网络相关的问题确实太复杂了,这里不可能介绍具体,因此如果想了解更多细节,还是建议参考相关资料。但Linux是一个网络原理学习和实践的好平台,不仅因为它本身对网络体系结构的实现是开放源代码的,而且各种相关的分析工具和函数库数不胜数,因此,如果你是学生,千万不要错过通过它来做相关的实践工作。
<span id="toc_5782_1744_2"></span>
## 网络原理介绍
<span id="toc_5782_1744_3"></span>
### 我们的网络世界
在进行所有介绍之前,来直观地感受一下那个真真实实存在的网络世界吧。当我在Linux下通过Web编辑器写这篇Blog时,一边用`mplayer`听着远程音乐,累了时则打开兰大的网络TV频道开始看看凤凰卫视……这些“现代化”的生活,我想,如果没有网络,将变得无法想象。
@@ -30,6 +50,7 @@
现在的网络应用越来越丰富多样,比如即时聊天(IM)、p2p资源共享、网络搜索等,它们是如何实现的,它们如何构建在各种各样的网络设备之上,并且能够安全有效的工作呢?这取决于这背后逐步完善的网络体系结构和各种相关网络协议的开发、实现和应用。
<span id="toc_5782_1744_4"></span>
### 网络体系结构和网络协议介绍
那么网络体系结构是怎么样的呢?涉及到哪些相关的网络协议呢?什么又是网络协议呢?
@@ -50,20 +71,24 @@
上面介绍了网络原理方面的基本内容,如果想了解更多网络原理和操作系统对网络支持的实现,可以考虑阅读后面的参考资料。下面将做一些实践,即在Linux下如何联网,如何用Linux搭建各种网络服务,并进行网络安全方面的考量以及基本的网络编程和开发的介绍。
<span id="toc_5782_1744_5"></span>
## Linux下网络“实战”
<span id="toc_5782_1744_6"></span>
### 如何把我们的Linux主机接入网络
如果要让一个系统能够联网,首先当然是搭建好物理网络了。接入网络的物理方式还是蛮多的,比如直接用网线接入以太网,用无线网卡上网,用ADSL拨号上网……
对于用以太网网卡接入网络的常见方式,在搭建好物理网络并确保连接正常后,可以通过配置IP地址和默认网关来接入网络,这个可以通过手工配置和动态获取两种方式。
<span id="toc_5782_1744_7"></span>
#### 范例:通过dhclient获取IP地址
如果所在的局域网有DHCP服务,那么可以这么获取,N是设备名称,如果只有一块网卡,一般是0或者1。
$ dhclient ethN
<span id="toc_5782_1744_8"></span>
#### 范例:静态配置IP地址
当然,也可以考虑采用静态配置的方式,ip\_address是本地主机的IP地址,gw\_ip\_address是接入网络的网关的IP地址。
@@ -73,16 +98,19 @@
如果上面不工作,记得通过`ifconfig/mii-tool/ethtool`等工具检查网卡是否有被驱动起来,然后通过`lspci/dmesg`等检查网卡类型(或者通过主板手册和独立网卡自带的手册查看),接着安装或者编译相关驱动,最后把驱动通过`insmod/modprobe`等工具加载到内核中。
<span id="toc_5782_1744_9"></span>
### 用Linux搭建网桥
网桥工作在OSI模型的第二层,即数据链路层,它只需要知道目标主机的MAC地址就可以工作。Linux内核在2.2开始就已经支持了这个功能,具体怎么配置看看后续[参考资料](http://www.ibm.com/developerworks/cn/linux/kernel/l-netbr/index.html)吧。如果要把Linux主机配置成一个网桥,至少需要两个网卡。
网桥的作用相当于一根网线,用户无须关心里头有什么东西,把它的两个网口连接到两个主机上就可以让这两个主机支持相互通信。不过它比网线更厉害,如果配上防火墙,就可以隔离连接在它两端的网段(注意这里是网络,因为它不识别IP),另外,如果这个网桥有多个网口,那么可以实现一个功能复杂的交换机,而如果有效组合多个网桥,则有可能实现一个复杂的可实现流量控制和负载平衡的防火墙系统。
<span id="toc_5782_1744_10"></span>
### 用Linux做路由
路由工作在OSI模型的第三层,即网络层,通过`router`可以配置Linux的路由,当然,Linux下也有很多工具支持动态路由的。相关的资料在网路中铺天盖地,由于时间关系,这里不做介绍。
<span id="toc_5782_1744_11"></span>
### 用Linux搭建各种常规的网络服务
需要什么网络服务呢?
@@ -96,12 +124,14 @@
What's more?如果你能想到,Linux上基本都有相应的实现。
<span id="toc_5782_1744_12"></span>
### Linux下网络问题诊断与维护
如果出现网络问题,不要惊慌,逐步检查网络的各个层次:物理链接、链路层、网络层直到应用层,熟悉使用各种如下的工具,包括`ethereal/tcpdump``hping``nmap``netstat``netpipe``netperf``vnstat``ntop`等。
关于这些工具的详细用法和网络问题诊断和维护的相关知识,请看后续相关资料。
<span id="toc_5782_1744_13"></span>
## Linux下网络编程与开发
如果想做网络编程开发,比如:
@@ -112,12 +142,14 @@ What's more?如果你能想到,Linux上基本都有相应的实现。
这个可以参考相关的Linux socket编程等资料。
<span id="toc_5782_1744_14"></span>
## 后记
本来介绍网络相关的一些基本内容,但因时间关系,没有详述,更多细节请参考相关资料。
到这里,整个《shell编程范例序列》算是很粗略地完成了,不过“范例”却缺少实例,特别是这一节。因此,如果时间允许,会逐步补充一些实例。
<span id="toc_5782_1744_15"></span>
## 参考资料
- 计算机网络——自上而下的分析方法
+12
View File
@@ -1,21 +1,33 @@
# 总结
- [前言](#toc_30143_27506_1)
- [Shell编程范例回顾](#toc_30143_27506_2)
- [常用Shell编程“框架”](#toc_30143_27506_3)
- [程序优化技巧](#toc_30143_27506_4)
- [其他注意事项](#toc_30143_27506_5)
<span id="toc_30143_27506_1"></span>
## 前言
到这里,整个Shell编程序列就要结束了,作为总结篇,主要回顾一下各个小节的主要内容,并总结出Shell编程的一些常用框架和相关注意事项等。
<span id="toc_30143_27506_2"></span>
## Shell编程范例回顾
TODO:主要回顾各小节的内容。
<span id="toc_30143_27506_3"></span>
## 常用Shell编程“框架”
TODO:通过分析一些实例总结各种常见问题的解决办法,比如如何保证同一时刻每个程序只有一个运行实体(进程)。
<span id="toc_30143_27506_4"></span>
## 程序优化技巧
TODO:多思考,总会有更简洁和高效的方式。
<span id="toc_30143_27506_5"></span>
## 其他注意事项
TODO:比如小心`rm -rf`的用法,如何查看系统帮助等。
+8
View File
@@ -1,5 +1,11 @@
# 简介
- [背景](#toc_18682_17841_1)
- [现状](#toc_18682_17841_2)
- [计划](#toc_18682_17841_3)
<span id="toc_18682_17841_1"></span>
## 背景
早在2007年11月,为了系统地学习和总结Shell编程,作者专门制定了一个Shell编程范例的总结计划,当时的计划是:
@@ -12,6 +18,7 @@
2008年4月底,整个序列大部分内容和框架基本完成,后来由于实习和工作原因,并没有持续完善。不过相关章节却获得了较好的反响,很多热心网友有大量评论和转载,例如,在百度文库转载的一份[《Shell编程范例之字符串操作》](http://wenku.baidu.com/link?url=WChC-PdR7rqPiwkuo70l2zGo3YVCFrG2cRRwMXsRh4niknUlAWd2eI-AMAiP2GbxhL-BFfV_CL65zUXjEXKYf0zVhF2AHZych7X1_b4pg47)的访问量已接近3000。说明整个序列还是有比较大的阅读群体。
<span id="toc_18682_17841_2"></span>
## 现状
考虑到整个Linux世界的蓬勃发展,Shell的使用环境越来越多,相关使用群体会不断增加,所以最近已经将该序列重新整理,并以自由书籍的方式发布,以便惠及更多的读者。
@@ -35,6 +42,7 @@
- 项目首页:<http://www.tinylab.org/project/pleac-shell/>
- 代码仓库:[https://github.com/tinyclub/open-shell-book.git](https://github.com/tinyclub/open-shell-book)
<span id="toc_18682_17841_3"></span>
## 计划
后续除了继续在[TinyLab.org](http://tinylab.org)以Blog形式持续更新外,还打算重新规划、增补整个序列,并以自由书籍的方式持续维护,并通过[TinLab.org](http://tinylab.org)平台接受读者的反馈,直到正式发行出版。