8月22日shell脚本编程之循环和函数

shell脚本编程

本章内容

编程基础
脚本基本格式
变量
运算
条件测试
流程控制
函数
数组
高级字符串操作
高级变量
配置用户环境

编程基础

程序:指令+数据
编程程序风格:
  过程式:以指令为中心,数据服务于指令
  对象式:以数据为中心,指令服务于数据
shell程序:提供了编程能力,解释执行

程序的执行方式

     计算机:运行二进制指令;
     编程语言:
    低级:汇编
    高级:
    编译:高级语言-->编译器-->目标代码
    java,C#
    解释:高级语言-->解释器-->机器代码
    shell, perl, python

编程基本概念

 编程逻辑处理方式:
顺序执行
循环执行
选择执行
 shell编程:过程式、解释执行
编程语言的基本结构:
数据存储:变量、数组
表达式: a + b
语句:if

shell脚本基础

 shell脚本是包含一些命令或声明,并符合一定格式的文
本文件
 格式要求:首行shebang机制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
 shell脚本的用途有:
 自动化常用命令
 执行系统管理和故障排除
 创建简单的应用程序
 处理文本或文件

创建shell脚本

 第一步:使用文本编辑器来创建文本文件
 第一行必须包括shell声明序列: #!
#!/bin/bash
 添加注释
注释以#开头
 第二步:运行脚本
 给予执行权限,在命令行上指定脚本的绝对或相对路径
 直接运行解释器,将脚本作为解释器程序的参数运行

脚本调试

bash -n /path/to/some_script
  检测脚本中的语法错误

bash -x /path/to/some_script
  调试执行

变量

 变量:命名的内存空间
数据存储方式:
字符:
数值:整型,浮点型
 变量:变量类型
作用:
1、数据存储格式
2、参与的运算
3、表示的数据范围
类型:
字符
数值:整型、浮点型

编程程序语言分类

 强类型:定义变量时必须指定类型、参与运算必须符合类型
要求;调用未声明变量会产生错误
如 java,python
 弱类型:无须指定类型,默认均为字符型;参与运算会自动
进行隐式类型转换;变量无须事先定义可直接调用
如: bash 不支持浮点数
 变量命名法则:
1、不能使程序中的保留字:例如if, for;
2、只能使用数字、字母及下划线,且不能以数字开头
3、见名知义
4、统一命名规则:驼峰命名法

bash中变量的种类

根据变量的生效范围等标准:
本地变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子进程均无效
环境变量:生效范围为当前shell进程及其子进程
局部变量:生效范围为当前shell进程中某代码片段(通常指函数)
位置变量:$1,$2..来表示,用于让脚本在脚本代码中调试通过命令行传递给它的参数
特殊变量:$?,$0,$*,$@,$#

本地变量

 变量赋值: name=‘value’,
 可以使用引用value:
(1) 可以是直接字串; name=“root"
(2) 变量引用: name="$USER"
(3) 命令引用: name=`COMMAND`, name=$(COMMAND)
 变量引用: ${name}, $name
"":弱引用,其中的变量引用会被替换为变量值
'':强引用,其中的变量引用不会被替换为变量值,而保
持原字符串
 显示已定义的所有变量: set
 删除变量: unset name

环境变量

 变量声明、赋值:
export name=VALUE
declare -x name=VALUE
 变量引用: $name, ${name}
 显示所有环境变量:
export
env
printenv
 删除: unset name
 bash有许多内建的环境变量: PATH, SHELL, USRE,UID,
HISTSIZE, HOME, PWD, OLDPWD, HISTFILE, PS1

只读和位置变量

只读变量:只能声时,但不能修改和删除
   readonly name
   declare -r name
位置变量:在脚本代码中调用通过命令行传递给脚本的参数
$1,$2..:对应第一、第二等参数,shift[n]换位置
$0:命令本身
$*:传递给脚本的所有参数,全部参数合为一个字符串
$@:传递给脚本的所有参数,每个参数为独立字符串
$#:传递给脚本的参数的个数
      $@,$*只在被双引号包起来的时候才会有差异
示例:判断给出的文件的行数
linecount="$(wc -l $1| cut -d' ' -f1)"
ho "$1 has $linecount lines."

算术运算

bash中的算术运算:help let
    +,-,*,/,%(取模),**(乘方)
实现算术运算:
(1)let var=算术表达式
(2)var=$[算术表达式]
(3)var=$((算术表达式))
(4)var=$(expr arg1 arg2 arg3..)
(5)declare -i var =数值
(6)echo '算术表达式'|bc
乘法符号有些场景中需要转义,如*
bash有内建的随机数生成器: $RANDOM( 1-32767)
  echo $[$RANDOM%50] : 0-49之间随机数

赋值

增强型赋值:
+=,-=,*=,/=,%=
let varOPERvalue
    例如:let count+=3
          自加3后自赋值
自增,自减:
let var+=1
let var++
let var-=1
let var--

逻辑运算

true,false
  1     0
与:
1 与 1 = 1
1 与 0 = 0
0 与 1 = 0
0 与 0 = 0
或:
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
非:!
! 1 = 0
! 0 = 1
短路运算:
短路与:
第一个为0,结果必定为0;
第一个为1,第二个必须要参与运算;
短路或:
第一个为1,结果必定为1;
第一个为0,第二个必须要参与运算;
异或: ^
异或的两个值,相同为假,不同为真

聚集命令

有两种聚集命令的方法:
复合式:date;who |wc -l
命令会一个接一个地运行
子shell:(date;who |wc -l) >> /tmp/trace
所有的输出都被发送给单个STDOUT和STDERR

退出状态

进程使用退出状态来报告成功或失败
 0 代表成功,1-255代表失败
 $?变量保存最近的命令退出状态
例如:
$ ping -c1 -W1 hostname &> /dev/null
$ echo $?

退出状态码

bash自定义退出状态码
exit [n]:自定义退出状态码;
注意:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

条件测试

 判断某需求是否满足,需要由测试机制来实现;
专用的测试表达式需要由测试命令辅助完成测试过程;
 评估布尔声明,以便用在条件性执行中
• 若真,则返回0
• 若假,则返回1
 测试命令:
• test EXPRESSION
• [ EXPRESSION ]
• [[ EXPRESSION ]]
注意: EXPRESSION前后必须有空白字符

条件性的执行操作符

 根据退出状态而定,命令可以有条件地运行
• && 代表条件性的AND THEN
• || 代表条件性的OR ELSE
 例如:
$ grep -q no_such_user /etc/passwd \
|| echo 'No such user'
No such user
$ ping -c1 -W2 station1 &> /dev/null \
> && echo "station1 is up" \
> || (echo 'station1 is unreachable'; exit 1)
station1 is up

test命令

 长格式的例子:
$ test "$A" == "$B" && echo "Strings are equal"
$ test “$A” -eq “$B” \
&& echo "Integers are equal"
 简写格式的例子:
$ [ "$A" == "$B" ] && echo "Strings are equal"
$ [ "$A" -eq "$B" ] && echo "Integers are equal"

bash的测试类型

数值测试:
-eq:是否等于
-ne:是不等于否
-gt:是否大于
-ge:是否大于等于
-lt:是否小于
-le:是否小于等于

bash的测试类型

 字符串测试:
==:是否等于;
>: ascii码是否大于ascii码
<: 是否小于
!=: 是否不等于
=~:左侧字符串是否能被右侧的PATHERN所匹配
注意:此表达式一般用于[[  ]]中;
-z "STRING":字符串是否为空,空为真,不空为假
-n "STRING":字符串是否不空,不空为真,空为假
注意:用于字符串比较时用到的操作数都应该使用引号

文件测试

 存在性测试
-a FILE:同-e
-e FILE: 文件存在性测试,存在为真,否则为假;
 存在性及类别测试
-b FILE:是否存在且为块设备文件;
-c FILE:是否存在且为字符设备文件;
-d FILE:是否存在且为目录文件;
-f FILE:是否存在且为普通文件;
-h FILE 或 -L FILE:存在且为符号链接文件;
-p FILE:是否存在且为命名管道文件;
-S FILE:是否存在且为套接字文件;
 文件权限测试:
-r FILE:是否存在且可读
-w FILE: 是否存在且可写
-x FILE: 是否存在且可执行
 文件特殊权限测试:
-g FILE:是否存在且拥有sgid权限;
-u FILE:是否存在且拥有suid权限;
-k FILE:是否存在且拥有sticky权限;
 文件大小测试:
-s FILE: 是否存在且非空;
 文件是否打开:
-t fd: fd表示文件描述符是否已经打开且与某终端相关
-N FILE:文件自动上一次被读取之后是否被修改过
-O FILE:当前有效用户是否为文件属主
-G FILE:当前有效用户是否为文件属组
 双目测试:
FILE1 -ef FILE2: FILE1与FILE2是否指向同一个设
备上的相同inode
FILE1 -nt FILE2: FILE1是否新于FILE2;
FILE1 -ot FILE2: FILE1是否旧于FILE2;

组合测试条件

 第一种方式:
COMMAND1 && COMMAND2 并且
COMMAND1 || COMMAND2 或者
! COMMAND 非
如: [ -e FILE ] && [ -r FILE ]
 第二种方式:
EXPRESSION1 -a EXPRESSION2 并且
EXPRESSION1 -o EXPRESSION2 或者
! EXPRESSION

使用read命令来接收输入

使用read来把输入值分配给一个或多个shell变量:
-p 指定要显示的提示
-t TIMEOUT  多久以后没输入命令自动退出
read 从标准输入中读取值,给每个单词分配一个变量,所有剩余的单词都被分配给最后一个变量
read -p “Enter a filename: “ FILE

流程控制

         过程式编程语言:
        顺序执行
        选择执行
        循环执行
    ###条件选择if语句
     选择执行:
     注意: if语句可嵌套
     单分支
    if 判断条件: then
         条件为真的分支代码
    fi
     双分支
    if 判断条件; then
        条件为真的分支代码
    else
        条件为假的分支代码
    fi
     多分支
    if CONDITION1; then
         if-true
    elif CONDITION2; then
         if-ture
    elif CONDITION3; then
         if-ture
    ...
    else
         all-false
    fi
     逐条件进行判断,第一次遇为“真”条件时,执行其分支,
    而后结束整个if语句
    条件判断:case语句
    case 变量引用 in
    PAT1)
         分支1
         ;;
    PAT2)
         分支2
         ;;
    ...
    *)
         默认分支
         ;;
    esac
    case支持glob风格的通配符:
    *: 任意长度任意字符
    ?: 任意单个字符
    []:指定范围内的任意单个字符
    a|b: a或b

循环

 循环执行
将某代码段重复运行多次
重复运行多少次:
循环次数事先已知
循环次数事先未知
有进入条件和退出条件
 for, while, until

for循环

 for 变量名 in 列表;do
      循环体
  done
 执行机制:
依次将列表中的元素赋值给“变量名” ; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
 列表生成方式:
(1) 直接给出列表
(2) 整数列表:
(a) {start..end}
(b) $(seq [start [step]] end)
(3) 返回列表的命令
$(COMMAND)
(4) 使用glob, 如: *.sh
(5) 变量引用;
$@, $*

while循环

 while CONDITION; do
       循环体
done
 CONDITION:循环控制条件;进入循环之前,先做一次判
断;每一次循环之后会再次做判断;条件为“ true”,则执行
一次循环;直到条件测试状态为“ false”终止循环
 因此: CONDTION一般应该有循环控制变量;而此变量的值
会在循环体不断地被修正
 进入条件: CONDITION为true;
 退出条件: CONDITION为false

until循环

 until CONDITION; do
        循环体
 done
 进入条件: CONDITION 为false
 退出条件: CONDITION 为true

循环控制语句continue

 用于循环体中
 continue [N]:提前结束第N层的本轮循环,而直接进入下一
轮判断;最内层为第1层
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
    continue
fi
CMDn
...
done

循环控制语句break

 用于循环体中
 break [N]:提前结束第N层循环, 最内层为第1层
while CONDTIITON1; do
       CMD1
...
if CONDITION2; then
       break
fi
      CMDn
...
done

创建无限循环

 while true; do
    循环体
 done
 until false; do
    循环体
 Done

特殊用法

 while循环的特殊用法(遍历文件的每一行):
while read line; do
      循环体
done < /PATH/FROM/SOMEFILE
 依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将
行赋值给变量line
 双小括号方法,即((…))格式,也可以用于算术运算
 双小括号方法也可以使bash Shell实现C语言风格的变量操作
#I=10
#((I++))
 for循环的特殊格式:
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
    循环体
done
 控制变量初始化:仅在运行到循环代码段时执行一次
 控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断

select循环与菜单

select variable in list
do
循环体命令
done
select 循环主要用于创建菜单,按数字顺序排列的
菜单项将显示在标准错误上,并显示 PS3 提示符,等待用户输入
 用户输入菜单列表中的某个数字,执行相应的命令
 用户输入被保存在内置变量 REPLY 中。

select与case

select 是个无限循环,因此要记住用 break 命令退
出循环,或用 exit 命令终止脚本。也可以按 ctrl+c退出循环。
select 经常和 case 联合使用
与 for 循环类似,可以省略 in list , 此时使用位置
参量

函数介绍

1、函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程
2、它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell序的一部分。
3、函数与shell程序比较相似,区别在于:
 Shell程序在子Shell中运行
 而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改

定义函数

 函数由两部分组成:函数名和函数体。
 语法一:
function f_name {
     ...函数体...
}

函数使用

函数的定义和使用:
    可在交互式环境下定义函数
    可将函数放在脚本文件中作为它的一部分
    可放在只包含函数的单独文件中
调用:函数只有被调用才会执行;
    调用:给定函数名
    函数名出现的地方,会被自动替换为函数代码
函数的生命周期:被调用时创建,返回时终止

函数返回值

函数有两种返回值;
函数的执行结果返回值:
   (1)使用echo或printf命令进行输出
   (2)函数体中调用命令的输出结果
函数的退出状态码:
   (1)默认取决于函数中执行的最后一条命令的退出状态码
   (2)自定义退出状态码,其格式为:
  return  从函数中返回,用最后状态命令决定返回值
  return 0 无错误返回
  return 1-255 有错误返回

交互式环境下定义和使用函数

 示例:
$dir() {
> ls -l
> }
 定义该函数后,若在$后面键入dir,其显示结果同ls -l的
作用相同。
$dir
 该dir函数将一直保留到用户从系统退出,或执行了如下
所示的unset命令:
$ unset dir

在脚本中定义及使用函数

函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用
调用函数仅使用其函数名即可。
示例:
$cat func1
#!/bin/bash
# func1
hello()
{
echo "Hello there today's date is `date +%F`"
}
echo "now going to the function hello"
hello
echo "back from the function"

使用函数文件

可以经常使用的函数存入函数文件,然后将函数文件载入shell。
文件名可任意选取,但最好与相关任务有某种联系。例如:function.main
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入次文件。

创建函数文件

$cat func1
#!/bin/bash
# func1
hello()
{
echo "Hello there today's date is `date +%F`"
}
echo "now going to the function hello"
hello
echo "back from the function"

载入函数

 函数文件已创建好后,要将它载入shell
 定位函数文件并载入shell的格式:
. filename 或 source filename
 注意:此即<点> <空格> <文件名>
这里的文件名要带正确路径
 示例:上例中的函数,可使用如下命令:
$ . functions.main

检测载入函数

 使用set命令检查函数是否已载入。 set命令将在shell中显示
所有的载入函数。
 示例:
$set
findit=( )
{
if [ $# -lt 1 ]; then
echo "usage :findit file";
return 1
fi
find / -name $1 -print
}

执行shell函数

 要执行函数,简单地键入函数名即可:
 示例:
$findit groups
/usr/bin/groups
/usr/local/backups/groups.bak

删除shell函数

 现在对函数做一些改动。首先删除函数,使其对shell不可用
。使用unset命令完成此功能.
 命令格式为:
 unset function_name
 实例:
$unset findit
再键入set命令,函数将不再显示

函数参数

 函数可以接受参数:
传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“ testfunc arg1 arg2 ...”
在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

函数变量

 变量作用域:
环境变量:当前shell和子shell有效
本地变量:只在当前shell进程有效,为执行脚本会启动
专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数。
局部变量:函数的生命周期;函数结束时变量被自动销毁
 注意:如果函数中有局部变量,如果其名称同本地变量, 使用局部变量。
 在函数中定义局部变量的方法
local NAME=VALUE

函数递归示例

 函数递归:
函数直接或间接调用自身
注意递归层数
 递归实例:
阶乘是基斯顿·卡曼于 1808 年发明的运算符号,是数学术语
一个正整数的阶乘( factorial)是所有小于及等于该数的正整
数的积,并且有0的阶乘为1。自然数n的阶乘写作n!。
n!=1×2×3×...×n。
阶乘亦可以递归方式定义: 0!=1, n!=(n-1)!×n。
n!=n(n-1)(n-2)...1
n(n-1)! = n(n-1)(n-2)!

 示例: fact.sh
#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact 5

作业

1、打印九九乘法表

月22日shell脚本编程之循环和函数"

月22日shell脚本编程之循环和函数"

2、利用变量RANDOM生成10个随机数字,输出这个10数字,并显示其中的最大者和最小者

3、打印国际象棋棋盘

月22日shell脚本编程之循环和函数"

4、写个脚本:

*




月22日shell脚本编程之循环和函数"

5、扫描/etc/passwd文件每一行,如发现GECOS字段为空,则填充用户名和单位电话为62985600,并提示该用户的GECOS信息修改成功

月22日shell脚本编程之循环和函数"

6函数

1、写一个服务脚本/root/bin/testsrv.sh,完成如下要求

(1) 脚本可接受参数: start, stop, restart, status

(2) 如果参数非此四者之一,提示使用格式后报错退出

(3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功”考虑:如果事先已经启动过一次,该如何处理?

(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成”考虑:如果事先已然停止过了,该如何处理?

(5) 如是restart,则先stop, 再start考虑:如果本来没有start,如何处理?

(6) 如是status, 则如果/var/lock/subsys/SCRIPTNAME文件存在,则显示“ SCRIPTNAME is running…”如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“ SCRIPTNAMEis stopped…”其中: SCRIPTNAME为当前脚本名

(7)在所有模式下禁止启动该服务,可用chkconfig 和 service命令管理

      1 #!/bin/bash
      2 #
      3 #
      4 cat << EOF
      5 start
      6 stop
      7 restart
      8 status
      9 ==================
     10 EOF
     11 
     12 read -p "please input options:" option
     13 
     14 while [ "$option" != "start" -a "$option" != "stop" -a "$option" != "restart" -a "$option" != "status" ];do
     15    read -p "please input options again:" option
     16 done
     17 
     18 file=/var/lock/subsys/SCRIPT_NAME
     19 
     20 start(){
     21     if [[ -f $file ]];then
     22          echo "this file has been exist"
     23     else
     24        touch $file
     25        echo "start success"
     26     fi
     27 }
     28 
     29 stop(){
     30    if [[ -f $file ]];then
     31        rm -f $file
     32        echo "stop success"
     33    else
     34       echo "this file has been disappion"
     35    fi
     36 }
     37 
     38 status(){
     39     if [ -z $file ];then
     40        echo "SCRIPT_NAME is running"
     41     else
     42        echo "SCRIPT_NAME is stopping"
     43     fi
     44 }
     45 
     46 case $option in
     47 start)
     48     start
     49     ;;
     50 stop)
     51     stop
     52     ;;
     53 restart)
     54     start
     55     stop
     56     ;;
     57 *)
     58     status
     59     ;;
     60 esac
     61 
     62

月22日shell脚本编程之循环和函数"

月22日shell脚本编程之循环和函数"

7、写一个函数实现两个数字做为参数,返回最大值

月22日shell脚本编程之循环和函数"

月22日shell脚本编程之循环和函数"

8、斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列: 0、 1、 1、 2、 3、 5、 8、 13、 21、 34、 ……,斐波纳契数列以如下被以递归的方法定义: F( 0) =0, F( 1) =1, F( n) =F(n-1)+F(n-2)( n≥2)写一个函数,求n阶斐波那契数列

月22日shell脚本编程之循环和函数"

月22日shell脚本编程之循环和函数"

原创文章,作者:15152188070,如若转载,请注明出处:http://www.178linux.com/38938

(0)
上一篇 2016-08-24 10:25
下一篇 2016-08-24 10:25

相关推荐

  • CentOS 7破解密码教程

    启动时任意键暂停启动  按e键进入编辑模式  将光标移动linux16开始的行,添加内核参数rd.break  按ctrl-x启动  mount –o remount,rw /sysroot  chroot /sysroot  passwd root  touch /.autorelabe…

    Linux干货 2016-12-01
  • N21天天第十周课程练习

    1、请详细描述CentOS系统的启动流程(详细到每个过程系统做了哪些事情) CentOS主机按以下顺序启动 1、POST加电自检 2、BIOS读取CMOS中的BIOS设置的参数来识别基础硬件,寻找到启动设备 3、MBR 1)读取启动设备MBR中前446字节的bootloader 2)读取MBR后的扇区用来识别grub以及内核kernel所在的区域 3)启动g…

    Linux干货 2016-09-26
  • Ansible (playbook)

    一、YAML 1.1 YAML介绍 YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001年在首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者。 YAML Ain’t Marku…

    Linux干货 2017-11-01
  • Linux程序包管理(一)RPM使用

    Linux程序包管理 在早期我们使用源代码的方式安装软件时,都需要先把源程序代码编译成可执行的二进制应用程序,然后进行安装。意味着每次安装软件都需要经过 预处理 –> 编译 –> 汇编–> 链接, 这个复杂的过程。为简化安装步骤,程序提供商就在特定的系统上面编译好相关程序的安装文件并进行打包,提…

    Linux干货 2016-06-01
  • 运维学习笔记-Puppet之Hiera初探

    为什么使用Hiera? Puppet中的manifest同时包含静态的代码(判断/循环逻辑,依赖关系,类定义,资源类型定义等等)和动态的数据(类声明时的参数值和资源声明时的属性值)。说代码是静态的是因为如果在设计阶段考虑比较全面,代码写成之后是很少变化的。但是数据要根据具体情况赋予不同的值。如果manifest设计的不是很灵活,比如某些数据被固化(hardc…

    Linux干货 2016-07-07
  • class14磁盘管理(一)

    一、硬盘基础知识及分区类型 1、磁盘结构 设备文件 I/O Ports: I/O 设备地址 一切皆文件:   open(), read(), write(), close() 设备类型: 块设备:block,存取单位“块”,磁盘 字符设备:char,存取单位“字符”,键…

    Linux干货 2016-08-29