Linux 环境中的程序设计基础

170
Linux 环环环环环环环环环环 环环 vi 环环环环

description

Linux 环境中的程序设计基础. 使用 vi 编辑文件. vi 编辑器使用. vi editor 是一个全屏幕文本编辑器 所有的 Linux 版本都提供了这一编辑器 在 Linux 系统中使用 vi 来编写文本文件 vi 也是 Unix 世界里极为普遍的全屏幕文本编辑器. vi 的启动. - PowerPoint PPT Presentation

Transcript of Linux 环境中的程序设计基础

Page 1: Linux 环境中的程序设计基础

Linux 环境中的程序设计基础

使用 vi编辑文件

Page 2: Linux 环境中的程序设计基础

vi 编辑器使用

vi editor 是一个全屏幕文本编辑器

所有的 Linux 版本都提供了这一编辑器

在 Linux 系统中使用 vi 来编写文本文件

vi 也是 Unix 世界里极为普遍的全屏幕文本编辑器

Page 3: Linux 环境中的程序设计基础

vi 的启动

格式:vi [options] [filename]

功能:自动载入所要编辑的已有文件,或开启一个新文件

输出:如果未指定选项或文件,将进入vi程序并开始编辑一个新缓冲区。当屏幕的左边出现“~”号,表示本行为空行。

常用选项 /功能:

+n 从第n行开始编辑文件

+/exp 从文件中匹配字符串exp的第一行开始编辑

Page 4: Linux 环境中的程序设计基础

shell 程序文件的建立实例

用 vi 编辑器建立 shell 程序文件 shex1 的步骤: ① 键入命令“ vi shex1” ,启动 vi ; ② 按命令“ i” ,进入 vi 的插入状态; ③ 输入程序文件内容;

clear cal 1 98 cal 2 98 cal 3 98

④ 按<Esc> 键,再键入“ :” ,切换到 vi 的命令状态; ⑤ 在“ :” 后键入命令“ wq” ,即保存文件内容后,退出 vi

Page 5: Linux 环境中的程序设计基础

vi的 2 种状态

进入 vi 后有 2 种状态:“编辑”状态和“一般命令”状态。  编辑状态  在此状态下输入文本编辑命令,输入文本文字信息。  一般命令状态 在此状态下键入的都是命令,这些命令用来下达一些文件存档、以及离开 vi 等等的操作指令。  当进入 vi ,先在编辑状态,等待编辑命令的输入。 2 种状态的转换  任何情况下,按“ ESC” 键就进入编辑命令状态,光标定位在文档中,等待编辑命令的输入。  在编辑命令状态下,按“:”键,进入一般命令状态,屏幕左下方出现“ :” 提示符,等待输入一般命令。

Page 6: Linux 环境中的程序设计基础

编辑状态下的文字输入

在编辑状态下,有光标闪动,这是文字插入点。在编辑状态下,输入以下编辑命令,便进入插入模式:

a :从光标所在位置后开始追加文字,光标后的文字随追加的文字向后移动。

i:从光标所在位置前开始插入文字,光标后的文字随追加的文字向后移动。

o:在光标所在行下方新增一行,并进入编辑状态。 编辑状态下输入文字时,有 2 种模式:“插入”与“改写”,按“ Insert” 键即可转换状态。编辑状态下按“ ESC” 键,就退出插入模式。此时输入“ /” ,进入一般命令状态,然后输入欲搜索的字符再按回车键,光标将指向文本中与字符匹配的地方。

Page 7: Linux 环境中的程序设计基础

   vi 下有多种编辑命令可完成以下功能:删除与恢复修改剪贴板查找与替换光标移动寻找、搜索……  但记住编辑命令是麻烦的,实际上,在命令模式下移动方向键就可以移动光标;按 [Delete] 键与[backspace] 键就可删除字符。

Page 8: Linux 环境中的程序设计基础

  在一般命令状态下,键入以下命令:

q 结束编辑(打开的文档未作任何修改),退出 vi

q! 放弃编辑不作保存(打开的文档已编辑过),退出 vi

wq 保存当前文件后退出 vi

wq! 强制性保存当前文件(包括只读文件)后退出 vi

w [filename] 用于对新建文件(直接键入 vi )或当前 正编辑的文件作保存后继续编辑e <filename> 当前没有未保存的修改,则进入由 < 文

件名 > 指出的文件进行编辑e! <filename> 即使有未保存的修改,也不作保存,进

入由 < 文件名 > 指出的文件进行编辑

退出 vi

Page 9: Linux 环境中的程序设计基础

删除与恢复

在编辑状态下,对文档内容作删除操作的命令:

x 删除当前光标所在位置的字符,功能同“ Del” 键。

dw 删除当前光标所在位置的单词。

dd 删除当前光标所在的行,注意: dd 是按两次 d。

d$ 删除当前光标所在位置至本行结尾的所有字符。

dG 删除当前光标所在位置至文件结尾处的所有字符。 恢复是指对误删除的恢复,在编辑状态下,若要恢复误删除的内容,则使用命令 u。

Page 10: Linux 环境中的程序设计基础

修改

R 键入 R ,然后键入修改后的单词。r 改写光标所在处的单个字符。~ 用来改变光标所在处的字符的大小写。

Page 11: Linux 环境中的程序设计基础

剪切、复制、粘贴

yy 将光标所处行选中,并复制到系统剪贴板上。

c 即 cut ,是将光标所处行移到剪贴板上,原行内容在屏幕上消失。

p 即 paste ,是将剪贴板上的内容复制到光标所在行。

Page 12: Linux 环境中的程序设计基础

查找和替换

vi 中也可进行查找和替换,这无疑为修改相同的字符串带来了方便, vi 的查找和替换功能是在命令方式下实现的,其命令如下:

s / 需替换内容 / 修改后内容 / 需改内容所在行号注意:在查找替换操作前,必须将光标移至文件首。

例:

用“ After completion” 来替换“ Once I have completed it.”

Page 13: Linux 环境中的程序设计基础

shell 程序运行的 2 种方法

运行 shell 程序有两种方法: 在 shell 环境下,将 shell 程序文件作为子 shell 程

序被调用执行

$sh <shell 程序文件名 > [< 参数 1> < 参数 2>

……]

例:$sh shex1

Page 14: Linux 环境中的程序设计基础

将 shell 程序文件视作命令来执行(因此, shell 程序文件可以看作是将各种命令组合在一起而形成的新命令),此时必须赋予 shell 程序文件执行权限

$ <shell 程序文件名 > [<参数 1> <参数 2> ……]

例:① $ chmod a+x shex1 给程序文件赋予可执行属性

② $ /home/user1/shex1 以完整的路径名键入程序名或 $ ./shex1 以相对路径名键入程序名

Page 15: Linux 环境中的程序设计基础

带参数的运行命令

运行命令中有 [ 参数 ] 和无 [ 参数 ] 的 2 种情况,分别称为带参数的运行命令和不带参数的运行命令

在 shell 程序中一般使用的形参为: $0、 $1、 $2、…… $9 ,共 10 个

当程序运行时,命令行中的 < 参数 1>、 < 参数2>… 依次赋值给 $1、 $2、…,通常称运行命令中所带的参数为实参

$0 是一个特殊的形参,其值规定为当前运行的 shell程序命令本身

Page 16: Linux 环境中的程序设计基础

例:建立 shell 程序 shex3 ,其内容为: clear

cal $1 98

cal $2 98

cal $3 98

执行时输入命令的形式为:$ sh shex3 1 2 3

第一个实参值是 1 ,运行时代换给程序中的形参 $1,

第二个实参值是 2 ,运行时代换给程序中的形参 $2,

第三个实参值是 3 ,运行时代换给程序中的形参 $3

Page 17: Linux 环境中的程序设计基础

命令 shift 作用

由于形参个数有限(一般为 10 个),当参数较多时,可通过命令 shift 来移动形参与实参的对应关系执行一次 shift 后, $1、 $2、……变为依次与< 参数 2> < 参数 3>…… 对应再执行一次 shift 后, $1、 $2、……变为依次与< 参数 3> < 参数 4>…… 对应,依此类推

Page 18: Linux 环境中的程序设计基础

例:建立 shell 程序 shex2 ,其内容为: echo $0 $1 $2 shift echo $0 $1 $2运行时情况如下:# /root/shex2 yesterday today tomorrow/root/shex2 yesterday today/root/shex2 today tomorrow#可见: $0 始终是当前运行的 shell 程序命令本身,即 /root/shex2

Page 19: Linux 环境中的程序设计基础

内部变量:由系统提供,用户不能修改。

用户变量:由用户建立和修改,在 shell 脚本编写中会经常用到。是在当前 shell 中使用的局部变量,不能被在 shell 下运行的其他命令或其它 shell 程序使用 环境变量:这些变量决定了用户工作的环境,它们不需要用户去定义,可以直接在 shell 中使用,其中某些变量用户可以修改。它可将值传给 shell 运行的其他命令或 shell 程序使用,也就是说系统变量是全局变量

shell 变量大致可以分为三类:内部变量、用户变量和环境变量。

Shell 变量

Page 20: Linux 环境中的程序设计基础

变量名 含义HOME 用户主目录UID 当前用户的标识符,取值是由数字构成的字符串USER 用户名,与登录名相同PWD 当前工作目录的绝对路径名,该变量的取值随 cd

命令的使用而变化MAIL 用户的邮箱路径名HOSTNAME 计算机的主机名INPUTRC 默认的键盘映像SHELL 用户所使用的 shell 的路径名HISTSIZE history 所能记住的命令的最多个数

常见的 Shell 变量

Page 21: Linux 环境中的程序设计基础

变量名 含义PATH shell 查找用户输入命令的路径 ( 目录列

表 ), shell 将按 PATH 变量中给出的顺序搜索这些目录,找到的第一个与命令名称一致的可执行文件将被执行

PS1 shell 一级命令提示符(主提示符)在特权用户下,默认的主提示符是 # ;在普通用户下,默认的主提示符是 $

PS2 shell 二级命令提示符在 shell 接收用户输入命令的过程中,如果用户在输入行的末尾输入“ \” 然后按回车键,或者当用户按回车键 shell 判断出用户输入的命令没有结束时,就显示这个辅助提示符,默认是“ >”

常见的 Shell 变量

Page 22: Linux 环境中的程序设计基础

PATH 变量是最重要的环境变量之一。当用户在命令行中输入命令时, shell 就会根据该变量定义的路径(目录)和顺序,查找并执行该命令。如果没有正确设置 PATH 变量,则必须输入完整的路径名来运行某个命令。

用户可以根据需要修改环境变量 如: HISTSIZE, PATH, PS1, PS2 等

在 Linux 下输入命令的两种方式: 直接在命令行中输入命令:根据 PATH 查找该命令 输入完整的路径名

Shell 变量

Page 23: Linux 环境中的程序设计基础

查询当前 shell 中的环境变量: env

env

查询某个变量的值: echo

echo ${ 变量名 }

Shell 变量查询

Page 24: Linux 环境中的程序设计基础

重设 PS1 和 PS2 的设置

例: export PS1=“\t\w\$”

在 bash 中,有两个级别的命令输入提示:

export使变量的值对当前 shell 及其所有子进程都可见

命令提示符

一级提示符是当 bash 等待输入命令时所出现的提示符,由环境变量 PS1 控制,缺省值为 “ $” ; 二级提示符是在 bash 执行一个命令后,需要用户进一步输入才能完成次命令时,所出现的提示符,由环境变量 PS2 控制,缺省值为 “ >” 。

Page 25: Linux 环境中的程序设计基础

在创建提示符时,可以使用下面的特殊字符:

\! 显示命令的历史编号 \h 显示机器的主机名\# 显示命令的命令编号 \s 显示当前使用的 shell

\\ 显示一个反斜杠 \u 显示用户名\n 显示一个换行符 \W 显示当前目录名\d 显示当前的日期 \w 显示当前目录完整路

径名\t 显示当前的时间\$ 用户显示“ $” 提示

符\nnn 显示与八进制 nnn

相对应的字符

命令提示符

Page 26: Linux 环境中的程序设计基础

在启动交互式会话过程中,在出现提示符前,系统会读取几个配置文件,并执行这些文件中的命令。所以这些文件可以用来定制 bash 环境。如:设置 shell 变量值或建立别名等。 bash 配置文件:

在命令行中设置和修改的变量值,只在当前的 shell 中有效。一旦用户退出 bash ,所做的一切改变都会丢失。

bash 配置文件

bash 配置文件

/etc/profile ~/.bash_profile~/.bash_login~/.profile

~/.bashrc

Page 27: Linux 环境中的程序设计基础

/etc/profile

bash 配置文件

读取 /etc/profile 文件后, bash 将在用户主目录中按顺序查找以下文件,并执行第一个找到的文件:

Linux 系统中的全局 bash 启动脚本,任何用户登录系统时 /etc/profile 都会被执行。通常用来设置标准 bash

环境,但修改该文件需 root 权限。

~/.bash_profile~/.bash_login~/.profile

在这些文件中,用户可以定义自己的环境变量,而且能够覆盖在 /etc/profile 中定义的设置。

Page 28: Linux 环境中的程序设计基础

bash 启动后,将读入配置文件 ~/.bashrc ,并执行这个文件中的所有内容。

bash 配置文件

另外,还可以从另一个 shell 或者 bash 自身启动一个新的 bash ,这种过程称为非登录交互式,启动新 bash

的命令为 bash ,此时所读入的唯一 bash 配置文件是 ~/.bashrc

通常,个人 bash 环境设置都定义在 ~/.bashrc 文件里

Page 29: Linux 环境中的程序设计基础

变量赋值: 等号两边不能有空格 如果要给变量赋空值,可以在等号后面跟一个换行符

variable=value

变 量

变量命名 变量名必须以字母或下划线开头,后面可以跟字母、数字 或下划线。任何其它字符都标志变量名的结束。 变量名关于大小写敏感。

变量类型: 根据变量的作用域,变量可以分为局部变量和环境变量 局部变量只在创建它们的 shell 中可用。而环境变量则 在所有用户进程中可用,通常也称为全局变量。

Page 30: Linux 环境中的程序设计基础

变量赋值

格式: < 变量名 > =< 字符串 >功能:将等号右边的字符串赋给等号左边的变量变量名可以由任意字符、数字或下划线组成,但数字不能作为变量名的首字符。变量名不能包含空格、惊叹号字符变量名不能使用 shell 中保留的关键词等符号变量名不能多于一个词,因为 shell 使用空格分析命令、定界命令名和参数,所以若字符串中含有空格,则在字符串上加上引号例: name1=MARY

name2="X Window"

Page 31: Linux 环境中的程序设计基础

显示变量的值echo $variable 或 echo ${variable}

清除变量unset variable

显示所有变量set

例:myname=jypanecho $mynameunset mynameecho $myname

变 量

Page 32: Linux 环境中的程序设计基础

变量举例

Page 33: Linux 环境中的程序设计基础

例: $ 是特殊变量,用来存储当前运行进程的 PID

局部变量和作用域 变量的作用域

是指变量在一个程序中那些地方可见。对于 shell 来说,局部变量的作用域限定在创建它们的 shell 中。

Page 34: Linux 环境中的程序设计基础

引用变量

格式: $< 变量名 >功能:引用变量的值。只要在变量名前加上“ $” 字符,变量名即将变量值取代例:# name2=$name1 将变量 name1 值赋予变量 name2 # echo $name1 在屏幕上显示变量 name1 的值

Page 35: Linux 环境中的程序设计基础

单引号和双引号允许用户一次把几个字符引起来。双引号不可以把美元符 $ 引起,因为 $ 操作符将导出变量所代表的值。如果已包含在双引号中有 $ 字符,并紧跟变量名,则在执行 shell 命令时,字符串中的变量将被变量所代表的数值所取代例:# winner=david# notice="The person who won is $winner."# echo $notice

显示结果为: The person who won is david.

Page 36: Linux 环境中的程序设计基础

但若用户不希望求出包含在字符串中的变量的值,在这种情况下,应该使用单引号作为字符串的定界符。单引号将抑制任何变量值,并把美元符看成另一个字符例:# winner=david# notice='The name is in the $winner variable.'# echo $notice

显示结果为: The name is in the $winner

variable.

Page 37: Linux 环境中的程序设计基础

若要将美元符使用,可以加上反斜杠 \ 来引导 $ 操作符例:# winner=david# result="$winner won \$100.00"# echo $result

显示结果为: david won $100.00.

Page 38: Linux 环境中的程序设计基础

释放变量

格式: unset < 变量名 >功能:释放一个现存的 shell 变量。要慎用 unset * ,因为它将删除全部 shell 变量例:# unset name1

Page 39: Linux 环境中的程序设计基础

例:

只读变量 只读变量

readonly variable

是指不能被清除或重新赋值的变量。

Page 40: Linux 环境中的程序设计基础

变量被创建时所处的 shell 称为父 shell 。如果在父 shell 中启动一个新的 shell (或进程),则该 shell (

或进程 ) 被称为子 shell ( 或子进程 ) 。

环境变量 环境变量 作用域包含创建它们的 shell ,以及从该 shell 产生的任意子 shell 或进程。 按照惯例,环境变量通常使用大写。 环境变量是已经用 export 内置命令输出的变量。

环境变量就象 DNA ,可以从父亲传递给儿子,再到孙子,但不能从子进程传递给父进程。

Page 41: Linux 环境中的程序设计基础

export :使变量的值对当前 shell 的所有子进程都可见。

说明:设置或显示环境变量。

语法: export [ 选项 ] [ 变量名称 ]=[ 变量设置值 ]

该命令的各选项含义如下:

-f 代表 [ 变量名称 ] 中为函数名称;

-n 删除指定变量,变量实际未删除,只是不输出到后续指令的执行环境中;

-p 列出所有 shell赋予程序的环境变量。

Page 42: Linux 环境中的程序设计基础

环境变量举例

export variable=value

设置环境变量

variable=value; export variable

例:

export -n variable

export -p

将全局变量转换成局部变量

列出所有全局变量

Page 43: Linux 环境中的程序设计基础

内置命令 declare 可用来创建变量。declare [ 选项 ] variable=value

declare 常用选项选项 含义-r 将变量设为只读 ( readonly )

-x 将变量输出到子 shell 中( export 为全局变量)

-i 将变量设为整型 ( integer )

-a 将变量设置为一个数组 ( array )

-f 列出函数的名字和定义 ( function )

-F 只列出函数名

内置命令 declare

Page 44: Linux 环境中的程序设计基础

declare –r myname=jypanunset mynamedeclare myname="Jianyu Pan"

declare –x myname2=pjy

myname2=pjydeclare –x myname2

declare

declare 举例

declare myname=jypan例:

Page 45: Linux 环境中的程序设计基础

printf 可用来按指定的格式输出变量printf format 输出参数列表

printf 的打印格式与 C 语言中的 printf 相同printf "%-12.5f\n" 123.456

format

以 % 开头 flag field width precision

格式符

-: 左对齐+: 输出符号0: 空白处添 0空格 : 前面加一空格

字段宽度 小数点后输出位数

printf 命令

Page 46: Linux 环境中的程序设计基础

printf 命令的格式说明符c 字符型 g/G 浮点数(自动)d 十进制整数 o 八进制e/E 浮点数(科学计数法) s 字符串f 浮点数(小数形式) x/X 十六进制

format 中还可以使用\a 警铃 \t 水平制表符\b 退后一格 \v 垂直制表符\n 换行 \\ 反斜杠\f 换页 \” 双引号\r 回车 %% 百分号

printf 命令

Page 47: Linux 环境中的程序设计基础

printf "The number is: %.2f\n" 100

printf "%-20s|%12.5f|\n" "Joy" 10

printf "%-10d%010o%+10x\n" 20 20 20

printf "%6d\t%6o\"%6x\"\n" 20 20 20

printf 命令举例

例:

Page 48: Linux 环境中的程序设计基础

shell 提供一些专用的修饰符来检查某个变量是否已被设置,然后根据测试结果指定变量的值,也称变量置换

${var:-word} 如果 var 存在且非空 , 则表达式 ${var:-word} 的值为 $var; 如果 var 未定义或为空值 , 则表达式的值为 word ,但 var 的值不变。

${var:=word} 如果 var 存在且非空 , 则表达式 ${var:=word} 的值为 $var; 如果 var 未定义或为空值 , 则表达式的值为 word ,且 var被赋值 word 。

${var:?word} 如果 var 存在且非空 , 则表达式的值为 $var;如果 var 未定义或为空值 , 则输出信息 word ,并终止脚本。

${var:+word} 如果 var 存在且非空 , 则表达式的值为 word;否则返回空值,但 var 的值不变。

变量测试

Page 49: Linux 环境中的程序设计基础

color=bluenewcolor=${color:-grey}

unset colorecho "The sky is ${color:-grey} today"echo $color

echo "The sky is ${color:=grey} today"echo $color

echo "The sky is ${color:?error} today"echo $color

echo "The sky is ${color:+blue} today"echo $color

变量测试举例

例:

Page 50: Linux 环境中的程序设计基础

位置参量是一组特殊的内置变量,通常被 shell

脚本用来从命令行接受参数,或被函数用来保存传递给它的参数。

执行 shell 脚本时,用户可以通过命令行向脚本传递信息,跟在脚本名后面的用空格隔开的每个字符串都称为位置参量。 在脚本中使用这些参数时,需通过位置参量来引用。例如: $1 表示第一个参数, $2 表示第二个参数,以此类推。 $9 以后需要用花括号把数字括起来,如第 10 个位置参量以 ${10} 的方式来访问。

位置参量(命令行参数)

Page 51: Linux 环境中的程序设计基础

$0 当前脚本的文件名$1-$9 第 1 个到第 9 个位置参量${10} 第 10 个位置参量,类似地,有 ${11} , ...

$# 位置参量的个数$* 以单字符串显示所有位置参量$@ 未加双引号时与 $* 含义相同,加双引号时有区别$$ 脚本运行的当前进程号$! 最后一个后台运行的进程的进程号$? 显示前面最后一个命令的退出状态。

0 表示没有错误,其他任何值表示有错误。$- 显示当前 shell 使用的选项

位置参量列表

Page 52: Linux 环境中的程序设计基础

例 1 :显示位置参量的值。 ( shprg1.sh )

例 2: $* 与 $@ 的区别:二者仅在被双引号括起来时有区别,此时前者将所有位置参量看成一个字符串,而后者将每个位置参量看成单独的字符串。 ( shprg2.sh )

位置参量举例

如果位置参量中含有空格,则需要使用双引号

args2.sh This is "Peter Piper"

Page 53: Linux 环境中的程序设计基础

shprg3.sh

basename

basename返回不含路径的文件名或目录名

basename /home/jypan/linux

basename ~

Page 54: Linux 环境中的程序设计基础

如何通过 str2 的值来引用 str1 的值 ? ( 间接引用 )

str1="Hello World"str2=str1echo $str2

echo $$str2 ?echo ${$str2} ?

eval newstr=\$$str2echo $newstr

newstr=${!str2} # bash2.0 以上才支持echo $newstr # echo ${!str2}

脚本范例: args3.sh Hello world!

变量的间接引用

Page 55: Linux 环境中的程序设计基础

listpage="ls -l | more"$listpage

listpage="ls -l | more"eval $listpage

eval newstr=\$$str2

eval

eval arg1 [arg2] ... [argN]

将所有的参数连接成一个表达式,并计算或执行该表达式,参数中的任何变量都将被展开。

Page 56: Linux 环境中的程序设计基础

命令替换的用处是将命令的输出结果赋给一个变量,或者用命令的输出结果代入命令所处的位置。 所有的 shell 都支持使用反引号来执行命令替换。

echo "The hostname is `hostname`"

Bash 除了使用反引号来执行命令替换外,还有另外一种替换方法:将命令放在前有美元符的一对圆括号内。

echo "The hostname is $(hostname)"

echo `basename \`pwd\`` echo $(basename $(pwd)) #see shprg4.sh

命令替换可以嵌套使用。 如果使用反引号,则内部的反引号必须用反斜杠来转义。

命令替换

Page 57: Linux 环境中的程序设计基础

Shell 脚本

Shell 脚本的编写

shell 脚本

当命令不在命令行中执行,而是从一个文件中执行时,该文件就称为 shell 脚本,即按顺序执行的 Linux 命令集。 shell 脚本按行解释。

Shell 脚本是纯文本文件,可以使用任何文本编辑器编写 Shell 脚本通常是以 .sh 作为后缀名 Shell 脚本的执行chmod +x script_name

./script_name

sh script_name

Page 58: Linux 环境中的程序设计基础

状态变量 $? 中保存命令退出状态的值

grep $USER /etc/passwdecho $?grep hello /etc/passwd; echo $?

条件测试

条件测试可以根据某个特定条件是否满足,来选择执行相应的任务。 Bash 中允许测试两种类型的条件: 命令成功或失败,表达式为真或假 任何一种测试中,都要有退出状态(返回值),退出状态为 0 表示命令成功或表达式为真,非 0 则表示命令失败或表达式为假。

Page 59: Linux 环境中的程序设计基础

内置测试命令 test 通常用 test 命令来测试表达式的值

x=5; y=10test $x -gt $yecho $?

test 命令可以用 方括号 来代替x=5; y=10[ $x -gt $y ] echo $?

表达式测试包括字符串测试、整数测试和文件测试。

测试表达式的值

方括号前后要留空格!

Page 60: Linux 环境中的程序设计基础

name=Tom [ $name = [Tt]?? ] echo $?

2.x 版本以上的 Bash 中可以用双方括号来测试表达式的值,此时可以使用通配符进行模式匹配。

测试表达式的值

[[ $name = [Tt]?? ]] echo $?

Page 61: Linux 环境中的程序设计基础

字符串测试[ -z str ] 如果字符串 str 长度为 0 ,返回真[ -n str ] 如果字符串 str 长度不为 0 ,返回

真[ str1 = str2 ] 两字符串相等[ str1 != str2 ] 两字符串不等

name=Tom; [ -z $name ]; echo $?

操作符两边必须留空格!

字符串测试

name2=Andy; [ $name = $name2 ] ; echo $?

Page 62: Linux 环境中的程序设计基础

整数测试,即比较大小[ int1 -eq int2 ] int1 等于 int2[ int1 -ne int2 ] int1 不等于 int2[ int1 -gt int2 ] int1 大于 int2[ int1 -ge int2 ] int1 大于或等于 int2[ int1 -lt int2 ] int1 小于 int2[ int1 -le int2 ] int1 小于或等于 int2

x=1; [ $x -eq 1 ]; echo $?

x=a; [ $x -eq 1 ]; echo $?

整数测试

操作符两边必须留空格!

X

Page 63: Linux 环境中的程序设计基础

整数测试也可以使用 let 命令或双圆括号

x=1; let "$x == 1"; echo $?

x=1; (($x+1>= 2 )); echo $?

只能用于整数测试!

整数测试

相应的操作符为:

== 、 != 、 > 、 >= 、 < 、 <=

例:

两种测试方法的区别 使用的操作符不同 let 和 双圆括号中可以使用算术表达式,而中括号不能 let 和 双圆括号中,操作符两边可以不留空格

Page 64: Linux 环境中的程序设计基础

逻辑测试[ expr1 -a expr2 ] 逻辑与,都为真时,结果为真[ expr1 -o expr2 ] 逻辑或,有一个为真时,结果为真

[ ! expr ] 逻辑非

x=1; name=Tom;

[ $x -eq 1 –a –n $name ]; echo $?

逻辑测试

注:不能随便添加括号

[ ( $x -eq 1 ) –a ( –n $name ) ]; echo $? X

Page 65: Linux 环境中的程序设计基础

x=1; name=Tom;

[[ $x -eq 1 && $name = To? ]]; echo $?

[[ pattern1 && pattern2 ]] 逻辑与

[[ pattern1 || pattern2 ]] 逻辑或

[[ ! pattern ]] 逻辑非

可以使用模式的逻辑测试

逻辑测试

Page 66: Linux 环境中的程序设计基础

文件测试:文件是否存在,文件属性,访问权限等。

-f fname fname 存在且是普通文件时,返回真 ( 即返回 0 )

-L fname fname 存在且是链接文件时,返回真 -d fname fname 存在且是一个目录时,返回真 -e fname fname (文件或目录)存在时,返回真 -s fname fname 存在且大小大于 0 时,返回真 -r fname fname (文件或目录)存在且可读时,返回真 -w fname fname (文件或目录)存在且可写时,返回真 -x fname fname (文件或目录)存在且可执行时,返回真

常见的文件测试操作符

更多文件测试符参见 test 的在线帮助

man test

文件测试

Page 67: Linux 环境中的程序设计基础

检查空值

[ "$name" = "" ]

[ ! "$name" ]

[ "X${name}" != "X" ]

检查空值

Page 68: Linux 环境中的程序设计基础

语法结构if expr1 # 如果 expr1 为真 (返回值为 0)then # 那么 commands1 # 执行语句块 commands1elif expr2 # 若 expr1 不真,而 expr2 为真then # 那么 commands2 # 执行语句块 commands2 ... ... # 可以有多个 elif 语句 else # else 最多只能有一个 commands4 # 执行语句块 commands4fi # if 语句必须以单词 fi 终止

if 条件语句

Page 69: Linux 环境中的程序设计基础

commands 为可执行语句块,如果为空,需使用 shell 提供的空命令 “ : ” ,即冒号。该命令不做任何事情,只返回一个退出状态 0 if 语句可以嵌套使用

ex4if.sh, chkperm.sh, chkperm2.sh, name_grep, tellme, tellme2, idcheck.sh

几点说明

elif 可以有任意多个( 0 个或多个) else 最多只能有一个( 0 个或 1 个) if 语句必须以 fi 表示结束 expr 通常为条件测试表达式;也可以是多个命令,以最后一个命令的退出状态为条件值。

Page 70: Linux 环境中的程序设计基础

语法结构case expr in # expr 为表达式,关键词 in 不要忘! pattern1) # 若 expr 与 pattern1 匹配,注意括号 commands1 # 执行语句块 commands1 ;; # 跳出 case 结构 pattern2) # 若 expr 与 pattern2 匹配 commands2 # 执行语句块 commands2 ;; # 跳出 case 结构 ... ... # 可以有任意多个模式匹配 *) # 若 expr 与上面的模式都不匹配 commands # 执行语句块 commands ;; # 跳出 case 结构esac # case 语句必须以 esac 终止

case 选择语句

Page 71: Linux 环境中的程序设计基础

case 语句举例: yes_no.sh

几点说明

每个命令块的最后必须有一个双分号,可以独占一行,或放在最后一个命令的后面。

所给的匹配模式 pattern 中可以含有通配符和“ | ”。

如果 expr 没有找到匹配的模式,则执行缺省值 “ *) ” 后面的命令块 ( 类似于 if 中的 else ) ; “ *) ” 可以不出现。

表达式 expr 按顺序匹配每个模式,一旦有一个模式匹配成功,则执行该模式后面的所有命令,然后退出 case。

Page 72: Linux 环境中的程序设计基础

语法结构for variable in list # 每一次循环,依次把列表 list 中的一个值赋给循环变量do # 循环开始的标志 commands # 循环变量每取一次值,循环体就执行一遍done # 循环结束的标志

几点说明 列表 list 可以是命令替换、变量名替换、字符串和文件名列表 ( 可包含通配符 )

for 循环执行的次数取决于列表 list 中单词的个数 for 循环体中一般要出现循环变量,但也可以不出现

for 循环语句

Page 73: Linux 环境中的程序设计基础

执行第一轮循环时,将 list 中的第一个词赋给循环变量,并把该词从 list 中删除,然后进入循环体,执行 do 和 done 之间的命令。下一次进入循环体时,则将第二个词赋给循环变量,并把该词从 list 中删除,再往后的循环也以此类推。当 list 中的词全部被移走后,循环就结束了。

循环执行过程

forloop.sh, mybackup.sh

位置参量的使用: $* 与 $@ greet.sh

可以省略 in list ,此时使用位置参量permx.sh tellme greet.sh / permx.sh *

for 循环执行过程

Page 74: Linux 环境中的程序设计基础

语法结构while expr # 执行 exprdo # 若 expr 的退出状态为 0,进入循环,否则退出while commands # 循环体done # 循环结束标志,返回循环顶部

执行过程

先执行 expr ,如果其退出状态为 0 ,就执行循环体。执行到关键字 done 后,回到循环的顶部, while 命令再次检查 expr 的退出状态。以此类推,循环将一直继续下去,直到 expr 的退出状态非 0 为止。

while 循环语句

Page 75: Linux 环境中的程序设计基础

语法结构until expr # 执行 exprdo # 若 expr的退出状态非 0,进入循环,否则退出until commands # 循环体done # 循环结束标志,返回循环顶部

执行过程与 while 循环类似,只是当 expr 退出状态非 0 时才执行循环体,直到 expr 为 0 时退出循环。

until 循环语句

Page 76: Linux 环境中的程序设计基础

用于强行退出当前循环。 如果是嵌套循环,则 break 命令后面可以跟一数字 n ,表示退出第 n 重循环(最里面的为第一重循环)。

用于忽略本次循环的剩余部分,回到循环的顶部,继续下一次循环。 如果是嵌套循环, continue 命令后面也可跟一数字 n ,表示回到第 n 重循环的顶部。

break [n]

continue [n]

例: months.sh

break 和 continue

Page 77: Linux 环境中的程序设计基础

sleep n

exit 和 sleep

exit n

exit 命令用于退出脚本或当前进程。 n 是一个从 0 到 255 的整数, 0 表示成功退出,非零表示遇到某种失败而非正常退出。该整数被保存在状态变量 $? 中。

exit 命令

sleep 命令

暂停 n 秒钟

Page 78: Linux 环境中的程序设计基础

语法结构

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

select variable in list do # 循环开始的标志 commands # 循环变量每取一次值,循环体就执行一遍done # 循环结束的标志

例: runit.sh

select 循环与菜单

Page 79: Linux 环境中的程序设计基础

例: goodboy.sh

select 经常和 case 联合使用

select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环。

与 for 循环类似,可以省略 in list ,此时使用位置参量

select 与 case

Page 80: Linux 环境中的程序设计基础

shift [n]

用于将参量列表 list 左移指定次数,缺省为左移一次。 参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。 while 循环遍历位置参量列表时,常用到 shift。

doit.sh a b c d e f g h

shft.sh a b c d e f g h

循环控制 shift 命令

例:

Page 81: Linux 环境中的程序设计基础

生成随机数的特殊变量echo $RANDOM

expr :通用的表达式计算命令表达式中参数与操作符必须以空格分开,表达式中的运算可以是算术运算,比较运算,字符串运算和逻辑运算。

expr 5 % 3

expr 5 \* 3 # 乘法符号必须被转义

随机数和 expr 命令

Page 82: Linux 环境中的程序设计基础

字符串操作${#var} 返回字符串变量 var 的长度${var:m} 返回 ${var}中从第 m个字符到最后的部分${var:m:len} 返回 ${var}中从第 m个字符开始,长度为 len的部

分${var#pattern} 删除 ${var}中开头部分与 pattern匹配的最小部

分${var##pattern}

删除 ${var}中开头部分与 pattern匹配的最大部分

${var%pattern} 删除 ${var}中结尾部分与 pattern匹配的最小部分

${var%%pattern}

删除 ${var}中结尾部分与 pattern匹配的最大部分

${var/old/new} 用 new替换 ${var}中第一次出现的 old

${var//old/new}

用 new替换 ${var}中所有的 old(全局替换 )

注: pattern, old 中可以使用通配符。 例: ex4str

m 的取值从 0 到 ${#var}-1

字符串操作

Page 83: Linux 环境中的程序设计基础

sh –x 脚本名该选项可以使用户跟踪脚本的执行,此时 shell 对脚本中每条命令的处理过程为:先执行替换,然后显示,再执行它。shell 显示脚本中的行时,会在行首添加一个加号 “ + ” 。

sh –v 脚本名在执行脚本之前,按输入的原样打印脚本中的各行。

sh –n 脚本名对脚本进行语法检查,但不执行脚本。如果存在语法错误, shell 会报错,如果没有错误,则不显示任何内容。

脚本调试

Page 84: Linux 环境中的程序设计基础

编程小结:变量 局部变量、环境变量( export、 declare -x)

只读变量、整型变量例: declare -i x; x="hello"; echo $x 0

位置参量( $0,$1,...,$*,$@,$#,$$,$?)

变量的间接引用( eval, ${!str})

例: name="hello"; x="name"; echo ${!x}

hello

命令替换( `cmd` 、 $(cmd))

整数运算 declare 定义的整型变量可以直接进行运算, 否则需用 let 命令或 $[...] 、 $((...)) 进行整数运算。

Page 85: Linux 环境中的程序设计基础

编程小结:输入输出 输入: read

read var1 var2 ...

read

read –p "提示 "

输出: printf

printf "%-12.5f \t %d \n" 123.45 8format以 %开头 flag field width

precision

格式符

-:左对齐+:输出符号0:空白处添 0空格 :前面加一空格

字段宽度

小数点后输出位数

cdefgsox

\b\n\r\t\v\\\”%%

REPLY

REPLY输出参数用空格隔开

Page 86: Linux 环境中的程序设计基础

字符串测试

编程小结:条件测试

[ -z string ] 如果字符串 string长度为 0,返回真[ -n string ] 如果字符串 string长度不为 0,返回真[ str1 = str2 ] 两字符串相等(也可以使用 == )[ str1 != str2 ] 两字符串不等

操作符两边必须留空格!

[[ str1 = str2 ]] 两字符串相等(也可以使用 == )[[ str1 != str2 ]] 两字符串不等[[ str1 > str2 ]] str1大于 str2,按 ASCII码比较[[ str1 < str2 ]] str1小于 str2,按 ASCII码比较

如果使用双方括号,可以使用 通配符 进行模式匹配。

例: name=Tom; [[ $name > Tom ]]; echo $?

Page 87: Linux 环境中的程序设计基础

编程小结:条件测试 整数测试[ int1 -eq int2 ] int1 等于 int2[ int1 -ne int2 ] int1 不等于 int2[ int1 -gt int2 ] int1 大于 int2[ int1 -ge int2 ] int1 大于或等于 int2[ int1 -lt int2 ] int1 小于 int2[ int1 -le int2 ] int1 小于或等于 int2

注意这两种方法的区别!

((int1 == int2)) int1 等于 int2((int1 != int2)) int1 不等于 int2((int1 > int2)) int1 大于 int2((int1 >= int2)) int1 大于或等于 int2((int1 < int2)) int1 小于 int2((int1 <= int2)) int1 小于或等于 int2

Page 88: Linux 环境中的程序设计基础

编程小结:条件测试 逻辑测试

如果使用双方括号,可以使用 通配符 进行模式匹配。

[[ pattern1 && pattern2 ]] 逻辑与

[[ pattern1 || pattern2 ]] 逻辑或

[[ ! pattern ]] 逻辑非

[ expr1 -a expr2 ] 逻辑与,都为真时,结果为真[ expr1 -o expr2 ] 逻辑或,有一个为真时,结果为真

[ ! expr ] 逻辑非

Page 89: Linux 环境中的程序设计基础

编程小结:条件测试

文件测试

-f fname fname 存在且是普通文件时,返回真 ( 即返回 0 )

-L fname fname 存在且是链接文件时,返回真 -d fname fname 存在且是一个目录时,返回真 -e fname fname (文件或目录)存在时,返回真 -s fname fname 存在且大小大于 0 时,返回真 -r fname fname (文件或目录)存在且可读时,返回真 -w fname fname (文件或目录)存在且可写时,返回真 -x fname fname (文件或目录)存在且可执行时,返回真

Page 90: Linux 环境中的程序设计基础

编程小结:控制结构

if 条件语句 case 选择语句 for 循环语句 while 循环语句 until 循环语句 break、 continue、 sleep 命令 select 循环与菜单 shift 命令

${...}, $(...), $[...], $

((...)) [...], [[...]], ((...))

各种括号的作用

Page 91: Linux 环境中的程序设计基础

条件判断命令 test

条件表达式是控制程序流程的关键, shell 提供的

test 命令适用于条件控制流和循环控制流

格式: test < 表达式 >

功能:返回表达式成立与否的状态值,

— 如果表达式成立,则 test 返回真的状态值 0 ,

— 反之, test 返回假的状态值,即一个非 0 值。

(常用的表达形式见书 P.147 )

Page 92: Linux 环境中的程序设计基础

例:判断文件 /etc 目录下文件 profile 是否存在 test -e /etc/profile当运行 shell 程序 shex 时,判断是否带参数 test -z $1( -z < 字符串 > 如字符串长度为 0 ,返回真值)判断变量 value 是否大于等于 1 并小于 3 test value –ge 1 -a value -lt 3( < 表达式 1> -a < 表达式 2> 与操作 < 数值表达式 1> -eq < 数值表达式 2> 等于返回真 < 数值表达式 1> -ge < 数值表达式 2> 大于或等于返回真)

Page 93: Linux 环境中的程序设计基础

条件控制流

① if-then-else 结构 if < 条件判断命令>

then < 命令集 1>

else < 命令集 2>

fi 通常采用“ test < 表达式 >” ,当条件成立,则返回 0 ;条件不成立,则返回一个非 0 值 注意:其中 if 和 fi 必须配对出现

  ② if-then-fi 结构

if < 条件判断命令 >

then < 命令集 >

fi

Page 94: Linux 环境中的程序设计基础

例:如果 /etc 目录中的文件 profile 存在,则将其复制到 /home 中,并分屏显示 /home 下的 profile 内容;否则在屯幕上显示信息“ profile is not exist!”

if test -e /etc/profile then cp /etc/profile /home cat /etc/profile|more else echo “profile is not exist!”fi

Page 95: Linux 环境中的程序设计基础

建立程序文件 shp3 ,存放在当前目录下。如果shp3 运行时未带参数,则在屏幕上显示信息“ Parameter is lost!” , 并结束程序运行;如果 shp3 运行时带一个参数,则判断参数所指定的文件是否存在,如果存在则复制该文件到 /home ;否则先在屏幕上显示信息“ File not found!” ,然后显示程序本身。

Page 96: Linux 环境中的程序设计基础

if test -z $1 -z判 $1存在否 then echo Parameter is lost! 如带一参数 else if test -e $1 则 $1 肯定在 then cp $1 /home else echo File not found! cat $0 $0 指程序本身 fifi

Page 97: Linux 环境中的程序设计基础

例:编写 shell 程序 shp6 ,存放在当前目录下。运行shp6 时带一个正整数参数,将数值存入变量 value ;如果该参数大于等于 1 但小于 3 ,则在屏幕上显示“ value is less than 3 and not less than 1”;如果该参数大于等于 3 ,则在屏幕上显示“ value is not less than 3”;最后以“ value=xx” 的形式显示变量 value 的值。

Page 98: Linux 环境中的程序设计基础

value=$1if test value -ge 1 -a value -lt 3 then echo value is less than 3 and not less than 1fiif test value -ge 3 then echo value is not less than 3fiecho value=$value

Page 99: Linux 环境中的程序设计基础

例:编 1 个 shell 程序 shp10 ,当以命令“ shp10 xx yy” 执行时,(其中 xx 为年份参数, yy 为季度参数),能自动清屏,并显示指定年份指定季度中的 3 个月的月历。

Page 100: Linux 环境中的程序设计基础

cleark=$2if test $k -eq 1 then cal 1 $1 cal 2 $1 cal 3 $1fiif test $k -eq 2 then cal 4 $1 cal 5 $1 cal 6 $1fi

if test $k -eq 3 then cal 7 $1 cal 8 $1 cal 9 $1fiif test $k -eq 4 then cal 10 $1 cal 11 $1 cal 12 $1fi

Page 101: Linux 环境中的程序设计基础

③ case 结构case < 变量 > in

字符串 1) < 命令集 1>

;;

字符串 n) < 命令集 n>

;;

*) < 缺省命令 >

;;

esac

说明:其中,字符串中可含通配符。如果能同时匹配多个字符串,则只能执行第一个匹配字

符串后的 < 命令集 > 。

Page 102: Linux 环境中的程序设计基础

循环控制流

① for-in 结构

for < 循环变量 > [in < 循环变量取值集 >]

do

< 命令集 >

done

  其中, < 循环变量取值集 > 中的值与值之间用空格分

隔。

Page 103: Linux 环境中的程序设计基础

例:用 for 命令实现,在当前目录下创建名为user0、 user1、… user9 十个子目录,用长格式显示这 10 个目录的目录信息,然后用 for 命令删去这 10个目录后,再用长格式显示这 10 个目录的目录信息。 for i in 0 1 2 3 4 5 6 7 8 9 do mkdir user$i done ls -dl user?|more for i in 0 1 2 3 4 5 6 7 8 9 do rm -rf user$i done ls -dl user?|more

Page 104: Linux 环境中的程序设计基础

例: (1) 清屏(2) 当程序运行时,屏幕显示如下形式信息:***********************This is a shell program ***********************(3) 检查 /home 目录下,是否存在由参数 1 指定的文件,若不存在,则屏幕显示信息“ File not found!” ;若存在,则将参数 1 指定的文件改名为由参数 2 指定的文件名,然后用 for 命令对改名后的文件显示其长格式的目录信息和文件内容

Page 105: Linux 环境中的程序设计基础

clearecho “**********************”echo “*This is a shell program *”echo “ **********************”

Page 106: Linux 环境中的程序设计基础

if test -n $1 -a -n $2 then if test $1!=$2 then if test -e $1 then mv $1 $2 for cn in “ls -l” “cat” do $cn $2 echo “” done          else echo File not found! fi     fifi

Page 107: Linux 环境中的程序设计基础

② while 结构 while < 条件判断命令 >

do

< 命令集 >

done

说明:当 < 条件判断命令 > 返回 0 时,则执行一轮由do和 done 括起来的循环体中的 < 命令集 > ,直到 <条件判断命令 > 返回一个非 0 值时,则不再执行循环体中的命令,执行 done 后面的命令

Page 108: Linux 环境中的程序设计基础

③ until 结构 until <条件判断命令 >

do

< 命令集>

done

执行过程说明:与 while 结构相反,当 < 条件判断命令 > 返回非0 时,执行循环体中的命令,直到 < 条件判断命令 > 返回一个 0 值时,继续执行 done 后面的命令。

Page 109: Linux 环境中的程序设计基础

shell 程序的调试

有两种简便的跟踪功能可以用于 shell 程序的调试

格式 1 : set -v

功能:当执行一个 shell 程序时,会在执行程序文件中的每条命令之前,自动在屏幕上先显示该条命令

格式 2 : set -x

功能:当执行 shell 程序时,它会把将要执行的命令逐条进行参量定值,并以最后执行的形式在屏幕上显示

Page 110: Linux 环境中的程序设计基础

Linux 环境中的程序设计基础

Linux编程环境

Page 111: Linux 环境中的程序设计基础

Linux C/C++ 编程C 语言是一种在 Unix 操作系统中被广泛使用的通用编程语言。用 C 语言写的程序执行速度很快。C 语言非常适合编写系统程序, Linux 内核就是使用 C 语言编写的。ANSI C的 C 语言标准

目标是为各种操作系统上的 C 程序提供可移植性保证,而不仅仅限于 Unix 。

不仅定义了 C 编程语言的语法和语义,还定义了一个标准库。

Page 112: Linux 环境中的程序设计基础

标准库可以根据头文件划分为 15 个部分,其中包括字符类型 <ctype.h> 、错误码<errno.h> 、浮点常数 <float.h> 、数学常数 <math.h> 、标准定义 <stddef.h> 、标准 I/O<stdio.h> 、工具函数<stdlib.h> 、字符串操作 <string.h> 、时间和日期 <time.h> 、可变参数表<stdarg.h> 、信号 <signal.h> 、非局部跳转 <setjmp.h> 、本地信息<local.h> 及程序断言 <assert.h> 等。

Page 113: Linux 环境中的程序设计基础

C 语言开发环境简介Linux 上广泛使用的 C 语言编译器是 GNU C 编译器, GNU C 建立在自由软件基金会的编程许可证的基础上,可以自由发布。在 Linux 下,一个完整的 C 语言开发环境到少包括以下三个组成部分:

函数库 glibc 编译器 gcc 系统头文件 glibc_header

glibc 是构成一个完整的 C 语言开发环境所必不可少的组成部分,也是 Linux下 C 语言的主要函数库。glibc_header 中包含了系统编译源代码所需要的声明文件 , 如果缺少系统头文件,很多用到系统功能的 C 程序将无法编译。

Page 114: Linux 环境中的程序设计基础

glibc 有两种安装方式: 安装成测试用的函数库在编译程序时用不同的选项来试用新的函数库。 安装成主要的 C 语言函数库所有新编译程序均使用的函数库。

gcc 是可以在多种硬件平台上编译出可执行程序的超级编译器, gcc 编译器能将 C 语言、 C++语言源程序、汇编语言程序和目标程序编译、连接成可执行文件。在 Linux 系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。而 gcc 则通过后缀来区别输入文件的类型。

Page 115: Linux 环境中的程序设计基础

gcc 支持编译的一些源文件的后缀及其说明:

后缀名 说明.c C 语言源代码文件.a 由目标文件构成的档案库文件

.C/.cc/.cxx C++ 源代码文件.h 程序所包含的头文件.i 已经预处理过的 C 语言源代码文件.ii 已经预处理过的 C++ 源代码文件.m Objective-C 语言源代码文件.o 编译后的目标文件.s 汇编语言源代码文件.S 经过预编译的汇编语言源代码文件

Page 116: Linux 环境中的程序设计基础

在 Linux 中, gcc(GNU C Compiler )是C 、 C++、Objective-C 源程序的编译器, gcc 编译 C源程序并生成可执行文件要经过以下四步:

( 1 )预处理gcc 编译器调用 cpp 程序,对各种命令如

#define、#include 进行分析。( 2 )编译gcc 编译器调用 ccl 程序,根据输入文件产生中间文件。( 3 )汇编gcc 编译器调用 as 程序,用中间文件作为输入产生

以 .o 作为类型名的目标文件。( 4 )连接gcc 编译器调用 ld 程序,将各目标程序组合于可执行文

件中的适当位置,这一程序引用的函数也放在可执行文件中。

Page 117: Linux 环境中的程序设计基础

C 函数库定义:是一些预先编译好的函数的集合,那些函数都是按照可再使用的原则编写的。它们通常是一组相互关联的用来完成某项常见工作的函数构成(比如 c 库里面的标准输入输出函数、时间函数和数学函数等)。函数库中的函数可以通过连接程序与应用程序进行连接,而不必在每次开发程序时都对这些通用的函数进行编译。不同类型的应用程序将会使用不同的函数库。如数学应用将使用数学库 libm, X-Window 应用程序将使用 Xlib 库及 libX11 。

Page 118: Linux 环境中的程序设计基础

所有的程序都将使用标准的 C 函数库 -libc ,该库中包含了内存管理或输入输出操作的基本函数,这些库都存放在 /usr/lib 这些系统公用的目录中,系统中的任何用户都可以利用这些库。库可以有三种使用的形式:

静态库:代码在编译时就已连接到开发人员开发的应用程序中。

共享库:只是在程序开始运行时才载入,在编译时,只是简单地指定需要使用的库函数。

动态库:是共享库的另一种变化形式,也是在程序运行时载入,使用的库函数不是在程序运行开始,而是在程序中的语句需要使用该函数时才载入。

Page 119: Linux 环境中的程序设计基础

动态库可以在程序运行期间释放动态库所占用的内存,腾出空间供其他程序使用。由于共享库和动态库并没有在程序中包括库函数的内容,只是包含了对库函数的引用,因此代码的规模比较小。已经开始的大多数库都采用共享库的方式。 ELF 格式的可执行文件使得共享库能够比较容易地实现,当然使用旧的 a.out 模式也可以实现库的共享。 Linux 系统中目前可执行文件的标准格式为 ELF 格式。

Page 120: Linux 环境中的程序设计基础

系统中可用的库都存放在 /usr/lib和 /lib目录中。库文件名由前缀 lib 和库名以及后缀组成。根据库的类型不同,后缀名也不一样。共享库的后缀名由 .so 和版本号组成,静态库名的后缀名为 .a或 .sa (采用旧的 a.out 格式的共享库)。

共享库名的格式:库的名称 .so.主版本号 . 次版本号 静态库名的格式:库的名称 .a例如:libm.so.5 数学共享库的标识字符为 m ,版本号为 5

libm.a 静态数字库

Page 121: Linux 环境中的程序设计基础

库文件与头文件的位置函数库存放的标准目录:/lib 系统必备共享库/usr/lib 标准共享库和静态库/usr/i486-linux-libc5/lib libc5 兼容性函数库/usr/X11R6/lib X11R6 的函数库/usr/local/lib 本地函数库头文件存放的目录:/usr/include 系统头文件/usr/local/include 本地头文件共享库及相关配置文件在 /etc 目录中,其中:ld.so.conf 包含共享库的搜索位置ldconfig 共享库管理工具,一般在更新了共享库之

后要运行该命令ldd 可查看可执行文件所使用的共享库

Page 122: Linux 环境中的程序设计基础

gcc 的基本用法和选项

格式: gcc [options] [filename]…

常用选项的说明:-ansi 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU

C 的某些特色, 例如 asm 或 typeof 关键词。 -c 对源程序进行预处理、编译,产生目标文件,但不进行

连接成为可执行文件。-DMACRO 以字符串“ 1” 定义 MACRO 宏。

-DMACRO=DEFN 以字符串“ DEFN” 定义

MACRO 宏。 -E 在预处理后停止,输出预处理后的源代码至标准输出,

不进行编译。

Page 123: Linux 环境中的程序设计基础

-g 产生一张用于调试和排错的扩展符号表,此选项使程序可用 gdb 进行调试。

-o < 文件名 > 定义输出的执行文件名为 < 文件名>

-S 在编译后停止,产生类型名为 s 的汇编代码文件,不生成中间文件。

-O 对程序编译进行优化,减少被编译程序的长度和执行时间,但此时的编译速度比不作优化慢且要求较多的内存。

-lobjc 可用于连接类型名为 o 的目标文件生成可执行文件。

-w 不生成任何警告信息-Wall生成所有警告信息。

Page 124: Linux 环境中的程序设计基础

/* 程序清单 main.c*/ #include <stdio.h> #include <stdlib.h>int factorial (int n); int main (int argc, char **argv) {

int n; if (argc < 2) {

printf ("Usage: %s n\n", argv [0]); return -1;

} else {

n = atoi (argv[1]); printf ("Factorial of %d is %d.\n", n, factorial (n));

}return 0;

}

假设有 两个源文件 main.c 和 factorial.c ,现在要编译生成一个计算阶乘的程序。

/* 程序清单 factorial.c*/#include <stdio.h>#include <stdlib.h> int factorial (int n) {

if (n <= 1) return 1;

else return

factorial (n - 1) * n; } $ gcc -o factorial main.c factorial.c

$ ./factorial 5

Factorial of 5 is 120.

Page 125: Linux 环境中的程序设计基础

GCC 可同时用来编译 C 程序和 C++ 程序。一般来说,C 编译器通过源文件的后缀名来判断是 C 程序还是 C+ + 程序。在 Linux 中, C 源文件的后缀名为 .c ,而 C++ 源文件的后缀名为 .C 或 .cpp 。 但是, gcc 命令只能编译 C++ 源文件,而不能自动和 C++ 程序使用的库连接。因此,通常使用 g++ 命令来完成 C++ 程序的编译和连接,该程序会自动调用 gcc 实现编译。 假设我们有一个如下的 C++ 源文件( hello.C ): #include <iostream.h>void main (void) {

cout << "Hello, world!" << endl; }

$ g++ -o hello hello.C

$ ./hello

Hello, world!

Page 126: Linux 环境中的程序设计基础

调试 C 程序当编译后得到的目标程序在运行时出现故障时,需要对你的源程序进行调试以找出错误,排除故障。Linux 提供了调试源程序的工具 gdb(GNU debugger )。GNU 的调试器称为 gdb ,该程序是一个交互式工具,工作在字符模式。在 X Window 系统中,有一个 gdb 的 前端图形工具,称为 xxgdb 。gdb 是功能强大的调试程序,可完成如下的调试任务:

设置断点; 监视程序变量的值; 程序的单步执行; 修改变量的值。

Page 127: Linux 环境中的程序设计基础

gdb 不仅允许用户在运行程序时显示源代码,而且在调试过程中允许在预设的断点处暂停程序的运行,显示变量的内容。在可以使用 gdb 调试程序之前,必须使用 -g 选项编译源文件。可在 makefile 中如下定义 CFLAGS 变量:

CFLAGS = -g运行 gdb 调试程序时通常使用如下的命令:

#gdb progname

Page 128: Linux 环境中的程序设计基础

在 gdb 提示符处键入 help ,将列出命令的分类,主要的分类有:

aliases :命令别名 breakpoints :断点定义 data :数据查看 files :指定并查看文件 internals :维护命令 running :程序执行 stack :调用栈查看 statu :状态查看 tracepoints :跟踪程序执行

键入 help 后跟命令的分类名,可获得该类命令的详细清单。

Page 129: Linux 环境中的程序设计基础

gdb 的常用命令 break NUM 在指定的行上设置断点。bt 显示所有的调用栈帧。该命令可用来显示函数的调用顺序clear 删除设置在特定源文件、特定行上的断点。其用法为:

clear FILENAME:NUM

continue 继续执行正在调试的程序。该命令用在程序由于处理信号或断点而导致停止运行时

display EXPR 每次程序停止后显示表达式的值。表达式由程序定义的变量组成

file FILE 装载指定的可执行文件进行调试help NAME 显示指定命令的帮助信息quit 退出 gdb 。也可以输入 'C-d' 来退出 gdb

run 执行载入后的要调试的程序。可以输入参数set 修改变量值。格式如下: set varible=value

step 单步执行,进入遇到的函数next 单步执行,不进入函数调用,即视函数调用为普通语句

Page 130: Linux 环境中的程序设计基础

info break 显示当前断点清单,包括到达断点处的次数等info files 显示被调试文件的详细信息info func 显示所有的函数名称info local 显示当函数中的局部变量信息info prog 显示被调试程序的执行状态info var 显示所有的全局和静态变量名称kill 终止正被调试的程序list 显示源代码段make 在不退出 gdb 的情况下运行 make 工具next 在不单步执行进入其他函数的情况下,向前执行一行源代码print EXPR 显示表达式 EXPR 的值

注意:在使用 gdb 调试程序前,必须使用 gcc –g 对源程序进行编译。

Page 131: Linux 环境中的程序设计基础

建立C源程序文件 star.c ,输出结果如下**********程序内容如下:#include <stdio.h>int main(){

int i=10 ; do

{printf (“ *” );++i ;

}while( i<10 ); printf (“ \n” ); }} 编译程序 star.c ,其目标程序以 star 命名且可用 gdb 进行调试

Page 132: Linux 环境中的程序设计基础

#gcc -o star -g star.c或#gcc -g star.c -o star#./star*为了查找该程序中的逻辑错误,使用 gdb 进行调试:#gdb ./starGNU gdb Red Hat Linux………(gdb)run*(gdb)whereNo stack

Page 133: Linux 环境中的程序设计基础

(gdb)list1 #include <stdio.h>2 int main()3 {4 int i=10 ;5 do6 {7 printf (“ *” );8 ++i ;9 }while( i<10 );10 printf (“ \n” );(gdb)break 1Breakpoint 1 at 0x8048370:file star.c,line 1.(gdb)runBreakpoint 1,main() at star.c:33 {

Page 134: Linux 环境中的程序设计基础

(gdb)step7 printf(“*”);(gdb)print i$1=7146752可见 i 的值为随机数,出错在于变量 i在 gdb 中,可以直接修改变量的值,为此在第 4 行处设置断点(gdb)clear 1Deleted breakpoint 1(gdb)break 4Breakpoint 2 at 0x8048380:file star.c,line 4.

Page 135: Linux 环境中的程序设计基础

(gdb)runThe program being debugged has been started already.Start it from the beginning?(y or n) yStarting program:/root/starBreakpoint 2,main() at star.c:44 int i=10;(gdb)step7 printf(“*”);(gdb)print i$2=10(gdb)set var i=0(gdb)print i$3=0(gdb)continue**********

Page 136: Linux 环境中的程序设计基础

Linux 编程风格

GNU 风格Linux 内核编程风格

Page 137: Linux 环境中的程序设计基础

GNU 风格 (1/2)函数返回类型说明和函数名分两行放置,函数起始字符和函数开头左花括号放到最左边。尽量不要让两个不同优先级的操作符出现在相同的对齐方式中,应该附加额外的括号使得代码缩进可以表示出嵌套。 按照如下方式排版 do-while 语句:

do{}while()

Page 138: Linux 环境中的程序设计基础

每个程序都应该以一段简短的说明其功能的注释开头。 请为每个函数书写注释,说明函数是做什么的,需要哪些入口参数,参数可能值的含义和用途。如果用了非常见的、非标准的东西,或者可能导致函数不能工作的任何可能的值,应该进行特殊说明。如果存在重要的返回值,也需要说明。 不要声明多个变量时跨行,每一行都以一个新的声明开头。当一个 if 中嵌套了另一个 if-else 时,应用花括号把 if-else 括起来。

Page 139: Linux 环境中的程序设计基础

GNU 风格要在同一个声明中同时说明结构标识和变量或者结构标识和类型定义 (typedef) 。先定义变量,再使用。 尽量避免在 if 的条件中进行赋值。 请在名字中使用下划线以分割单词,尽量使用小写;把大写字母留给宏和枚举常量,以及根据统一惯例使用的前缀。例如,应该使用类似ignore_space_change_flag 的名字;不要使用类似 iCantReadThis 的名字。用于表明一个命令行选项是否给出的变量应该在选项含义的说明之后,而不是选项字符之后被命名。

Page 140: Linux 环境中的程序设计基础

Linux 内核编程风格 Linux 内核风格采用 K&R 标准,将开始的大括号放在一行的最后,而将结束的大括号放在一行的第一位。命名尽量简洁。

不应该使用诸如ThisVariableIsATemporaryCounter 之类的名字。应该命名为 tmp ,这样容易书写,也不难理解。

命名全局变量,应该用描述性命名方式,例如应该命名“ count_active_users()” ,而不是“ cntusr()” 。

本地变量应该避免过长。

Page 141: Linux 环境中的程序设计基础

Linux 内核缩进风格是 8 个字符。函数最好短小精悍,一般来说不要让函数的参数多于 10 个,否则应该尝试分解这个过于复杂的函数。 通常情况,注释说明代码的功能,而不是其实现原理。避免把注释插到函数体内,而写到函数前面,说明其功能,如果这个函数的确很复杂,其中需要有部分注释,可以写些简短的注释来说明那些重要的部分,但是不能过多。

Page 142: Linux 环境中的程序设计基础

Linux 下函数库

一个“程序函数库”就是一个文件包含了一些编译好的代码和数据,这些编译好的代码和数据可以在事后供其他的程序使用。程序函数库可以使整个程序更加模块化,更容易重新编译,而且更方便升级。可分为两种类型:

Page 143: Linux 环境中的程序设计基础

静态函数库 (static libraries) :是一个普通的目标文件的集合,一般用“ .a” 作为文件的后缀。静态函数库和共享函数库相比有很多的缺点,占用内存空间多。但使用 ELF 格式的静态库函数生成的代码可以比使用共享函数库的程序运行速度上快一些。

可以用 ar 这个程序来创建一个静态函数库文件,或者往一个已经存在地静态函数库文件添加新的目标代码。

例如 , 把 file1.o和 file2.o 加入到my_library.a 这个函数库文件: ar rcs my_library.a file1.o file2.o

然后运行 ranlib ,以给库加入一些索引信息

Page 144: Linux 环境中的程序设计基础

共享函数库 (shared libraries) :当一个可执行程序在启动的时候被加载的函数。每个共享函数库都有个特殊的名字,称作“ soname”。 soname 名字命名必须以“ lib” 作为前缀,然后是函数库的名字,然后是“ .so” ,最后是版本号信息。优点:多进程使用同一函数库;修改函数库不需重新链接。安装一个新版本的函数库的时候,要先将这些函数库文件拷贝到一些特定的目录中,运行 ldconfig 就可以。 ldconfig检查已经存在的库文件,然后创建 soname的符号链接到真正的函数库,同时设置 /etc/ld.so.cache这个缓冲文件。 例如,创建两个目标文件 (a.o和 b.o) ,然后创建一个包含 a.o和 b.o 的共享函数库。

Page 145: Linux 环境中的程序设计基础

gcc -fPIC -g -c -Wall a.cgcc -fPIC -g -c -Wall b.cgcc -shared -Wl,-soname,liblusterstuff.so.1 -o liblusterstuff.so.1.0.1 a.o b.o –lc注:” -fPIC ” 是位置无关参数, ” -g”和“- Wall” 参数不是必须的。

Page 146: Linux 环境中的程序设计基础

Linux 下函数库函数库和头文件的保存位置

a. 函数库 /lib :系统必备共享函数库 /usr/lib :标准共享函数库和静态函数库 /usr/i486-linux-libc5/lib: libc5 兼容性函数库

/usr/X11R6/lib: X11R6 的函数库 /usr/local/lib :本地函数库

Page 147: Linux 环境中的程序设计基础

b. 头文件 /usr/include :系统头文件 /usr/local/include :本地头文件

c.  共享函数库的相关配置和命令 /etc/ld.so.conf :包含共享库的搜

索位置 ldconfig :共享库管理工具,一般在更新了共享库之后要运行该命令

ldd :可查看可执行文件所使用的共享函数库

Page 148: Linux 环境中的程序设计基础

使用 GNU cc 开发应用程序

gcc 的简介 可执行文件的格式 gcc 的使用

Page 149: Linux 环境中的程序设计基础

gcc 的简介

gcc 是 GNU 的 C 和 C++ 编译器。实际上, gcc 能够编译多种语言: C 、 C++ 和 Object C 等。利用 gcc 命令可同时编译并连接 C 和 C++ 源程序。也可以对几个 C 源文件利用 gcc 编译、连接并生成可执行文件。gcc 可以使程序员灵活地控制编译过程。编译过程一般可以分为下面四个阶段,每个阶段分别调用不同的工具进行处理

预处理 链接编译 组译源 程 序(*.c)

可执行文件

预处理器 编译器 组译器 连接器

Page 150: Linux 环境中的程序设计基础

gcc 的四个阶段命令 gcc首先调用 cpp 进行预处理,在预处理过程中,对源代码文件中的文件包含 (include) 、预编译语句 ( 如宏定义 define等 ) 进行分析。接着调用 cc1 进行编译,这个阶段根据输入文件生成以 .o为后缀的目标文件。汇编过程是针对汇编语言的步骤,调用 as 进行工作,一般来讲, .S或 .s 为后缀的汇编语言源代码文件汇编之后都生成以 .o 为后缀的目标文件。当所有的目标文件都生成之后, gcc 就调用 ld 来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的函数库中连到合适的地方。

Page 151: Linux 环境中的程序设计基础

可执行文件格式 Linux 系统中可执行文件有两种格式。

第一种格式是 a.out 格式,这种格式用于早期的 Linux 系统以及 Unix 系统的原始格式。 a.out 来自于 Unix C 编译程序默认的可执行文件名。当使用共享库时, a.out 格式就会发生问题。把 a.out 格式调整为共享库是一种非常复杂的操作。

因此,一种新的文件格式被引入 Unix 系统 5 的第四版本和Solaris 系统中。它被称为可执行和连接的格式( ELF )。这种格式很容易实现共享库。

ELF 格式已经被 Linux 系统作为标准的格式采用。gcc 编译程序产生的所有的二进制文件都是 ELF 格式的文件(即使可执行文件的默认名仍然是 a.out )。较旧的 a.out 格式的程序仍然可以运行在支持 ELF 格式的系统上。

Page 152: Linux 环境中的程序设计基础

GNU C 的使用基本语法 gcc [options] [filenames]说明:

在 gcc 后面可以有多个编译选项,同时进行多个编译操作。很多的 gcc 选项包括一个以上的字符。因此你必须为每个选项指定各自的连字符。例如,下面的两个命令是不同的:

gcc -p -g test1.c gcc -pg test1.c

当你不用任何选项编译一个程序时, GCC 将会建立( 假定编译成功 ) 一个名为 a.out 的可执行文件。

Page 153: Linux 环境中的程序设计基础

GCC 的警告选项在 gcc 中用开关 -Wall 控制警告信息,使用示例命令如下:

gcc –Wall -o test3_1 test3_1.c-w 不生成任何警告信息。

Page 154: Linux 环境中的程序设计基础

查找选项 gcc 一般使用默认路径查找头文件和库文件。如

果文件所用的头文件或库文件不在缺省目录下,则编译时要指定它们的查找路径。 -I 选项:指定头文件的搜索目录

例:gcc –I/export/home/st –o test1 test1.c -L 选项:指定库文件的搜索目录

例:gcc –L/usr/X11/R6/lib –o test1 test1.c

Page 155: Linux 环境中的程序设计基础

多个源文件生成一个可执行文件

问题:有多个源文件时,如何生成一个可执行文件? 方法1 : gcc –Wall –o mytest test1.c test2.c

test3.c 方法2 :

gcc-Wall -c test1.cgcc-Wall –c test2.cgcc-Wall –c test3.c gcc –o mytest test1.o test2.o

test3.o

Page 156: Linux 环境中的程序设计基础

优化选项

优化选项可以使 GCC 在耗费更多编译时间和牺牲易调试性的基础上产生更小更快的可执行文件。这些选项中最典型的是 -O和 -O2 选项。

-O0 不进行优化处理。 -O 选项:告诉 GCC 对源代码进行基本优化。这

些优化在大多数情况下都会使程序执行的更快。 -O2 选项:告诉 GCC 产生尽可能小和尽可能快

的代码。 -O2 选项将使编译的速度比使用 -O 时慢。但通常产生的代码执行速度会更快。

-O3 选项:比 -O2 更进一步优化,包括 inline 函数。

Page 157: Linux 环境中的程序设计基础

版本选项-v 选项

用户将会得到自己目前正在使用的 gcc 的版本及与版本相关的一些信息。

gcc -v 将得到如下结果: Reading specs from /usr/lib/gcc-lib/i486-

box-linux/2.7.2/specs gcc version 2.7.2

-V 选项 如果安装了多个版本的 gcc ,并且想强制执行其中

的某个版本,可以用命令通知系统用户要使用的版本。 gcc -V2.6.3 -v

Page 158: Linux 环境中的程序设计基础

宏定义选项

-D MACRO 以字符串“ 1” 定义 MACRO 宏。 -D MACRO=DEFN 以字符串“ DEFN” 定义 MACRO 宏。 -U MACRO 取消对 MACRO 宏的定义。

Page 159: Linux 环境中的程序设计基础

调试和剖析选项 使用调试选项后, gcc 在进行编译的时候,在目标文件( .o )和创建的可执行文件中插入额外信息,这些额外信息使 gdb 能够判断编译过的代码和源代码之间的关系。-g 选项:告诉 GCC产生能被 GNU 调试器使用的调试信息以便调试你的程序。

例: gcc –g –o test3 test3.c-pg 选项:告诉 GCC 在你的程序里加入额外的代码,执行时,产生 gprof 用的剖析信息以显示你的程序的耗时情况。使用 gdb调试工具,命令行如下: 例: gcc –ggdb3 –o test3 test3.c

Page 160: Linux 环境中的程序设计基础

gdb 的启动在命令行上输入 gdb 并按回车键就可以运行 gdb 了,如果一切正常的话,将启动 gdb

gdb [filename]出现(gdb)在这里,可以输入调试命令

在可以使用 gdb 调试程序之前,必须使用 -g 选项编译源文件。可在 makefile 中如下定义 CFLAGS 变量: CFLAGS = -g 运行

Page 161: Linux 环境中的程序设计基础

获取帮助信息启动 gdb 后,可以在命令行上指定很多的选项。输入:

help可以获得 gdb 的帮助信息。

如果想要了解某个具体命令(比如 break )的帮助信息,在 gdb 提示符下输入下面的命令:

break屏幕上会显示关于 break 的帮助信息。从返回的信息

可知, break 是用于设置断点的命令。另一个获得 gdb 帮助的方法是浏览 gdb 的手册页。在Linux Shell 提示符输入: man gdb

可以看到 man 的手册页

Page 162: Linux 环境中的程序设计基础

gdb 命令的分类在 gdb 提示符处键入 help ,将列出命令的分类,主要的分类有:

aliases :命令别名 breakpoints :断点定义; data :数据查看; files :指定并查看文件; internals :维护命令; running :程序执行; stack :调用栈查看; statu :状态查看; tracepoints :跟踪程序执行。

后跟命令的分类名,可获得该类命令的详细清单

Page 163: Linux 环境中的程序设计基础

基本 gdb 命令

file 命令:装入想要调试的可执行文件。 cd 命令:改变工作目录。 pwd 命令:返回当前工作目录。 run 命令:执行当前被调试的程序。 kill 命令:停止正在调试的应用程序。 list 命令:列出正在调试的应用程序的源代码。 break 命令:设置断点。

Page 164: Linux 环境中的程序设计基础

watch 命令:设置监视点,监视表达式的变化。 awatch 命令:设置读写监视点。当要监视的表达式被读或写时将应用程序挂起。它的语法与watch 命令相同。 rwatch 命令:设置读监视点,当监视表达式被读时将程序挂起,等侍调试。此命令的语法与watch 相同。 next 命令:执行下一条源代码,但是不进入函数内部。也就是说,将一条函数调用作为一条语句执行。执行这个命令的前提是已经 run ,开始了代码的执行。

Page 165: Linux 环境中的程序设计基础

step 命令:执行下一条源代码,进入函数内部。如果调用了某个函数,会跳到函数所在的代码中等候一步步执行。执行这个命令的前提是已经用 run 开始执行代码。 display 命令:在应用程序每次停止运行时显示表达式的值。info break 命令:显示当前断点列表,包括每个断点到达的次数16) info files 命令:显示调试文件的信息。 17) info func 命令:显示所有的函数名。

Page 166: Linux 环境中的程序设计基础

18) info local 命令:显示当前函数的所有局部变量的信息。 19) info prog 命令:显示调试程序的执行状态。 20) print 命令;显示表达式的值。 21) delete 命令:删除断点。指定一个断点号码,则删除指定断点。不指定参数则删除所有的断点。 22) Shell 命令:执行 Linux Shell 命令。 23)make 命令:不退出 gdb 而重新编译生成可执行文件。 24)Quit 命令:退出 gdb 。

Page 167: Linux 环境中的程序设计基础

gdb 使用实例/* 一个有错误的 C 源程序 */

#include <stdio.h> #include <stdlib.h> static char buff [256]; static char* string; int main () { printf ("Please input a string: "); gets (string); printf ("\nYour string is: %s\n", string); }

Page 168: Linux 环境中的程序设计基础

上面这个程序非常简单,其目的是接受用户的输入,然后将用户的输入打印出来。该程序使用了 一个未经过初始化的字符串地址 string ,因此,编译并运行之后,将出现 Segment Fault 错误:

$ gcc -o test -g test.c $ ./test Please input a string: asfd Segmentation fault (core dumped)

Page 169: Linux 环境中的程序设计基础

gdb 使用实例为了查找该程序中出现的问题,我们利用 gdb ,并按如下的步骤进行:

1 .运行 gdb bugging 命令,装入 bugging 可执行文件;

2 .执行装入的 bugging 命令; 3 .使用 where 命令查看程序出错的地方; 4 .利用 list 命令查看调用 gets 函数附近的代

码;

Page 170: Linux 环境中的程序设计基础

5 .唯一能够导致 gets 函数出错的因素就是变量 string 。用 print 命令查看 string 的值;

6 .在 gdb 中,我们可以直接修改变量的值,只要将 string 取一个合法的指针值就可以了,为 此,我们在第 11 行处设置断点;

7 .程序重新运行到第 11 行处停止,这时,我们可以用 set variable 命令修改 string 的取值;

8 .然后继续运行,将看到正确的程序运行结果。