linux鸟哥的私房菜名字

&& 今天是除夕,祝大家新年快乐,蛇年行大运啰,各位喜欢鸟哥网站的朋友们,天天都幸福!!
&& 新增网页计数器与更新一些基础篇连结
&& 服务器更新第一版
&& 服务器更新至第三版
&& 新版基础篇上线,服务器目前更新至第二版
&& 完成第二版首頁
is designed by
本网页主要以Firefox配合解析度
作为设计依据&&&& 鸟哥自由软件整合应用研究室鸟哥的linux私房菜上&第三部分学习shell与shell&script
第10章 vim程序编辑器
1、Linux下面的配置文件多为文本文件,故使用vim即可进行设置编辑。
2、vim可视为程序编辑器,可用以编辑shell script、配置文件等,避免打错字。
3、vi为所有UNIX like的操作系统都会存在的编辑器,且执行速度快。
4、vi有三种模式,一般模式可变换到编辑与命令行模式,但编辑模式与命令行模式不能互换。
5、常用的按键有i,[Esc],:wq等。
6、vi的界面大致可分为两部分:上半部的文本与最后一行的状态+命令行模式。
7、数字是有意义的,用来说明重复进行几次操作的意思,如5yy为副职5行的意思。
8、光标的移动中,大写的G经常使用,尤其是1G,G,表示移动到文章的头、尾功能。
9、vi的替换功能也很棒。:n1,n2s/old/new/g要特别注意学习起来。
10、小数点“.”为重复进行前一次操作,也是经常使用的按键功能。
11、进入编辑模式几乎只要记住i,o,R三个按钮即可,尤其是新增一行的o与替代的R。
12、vim会主动创建swap暂存文件,所以不要随意断线。
13、可以使用ctrl -v进行复制/粘贴/删除的行为。块选择。
y:将反白的地方复制起来
d:将反白的地方删除
& & vim hosts
/etc/hosts 多文件编辑
14、使用:sp功能可以切割窗口。
& & 命令模式输入
& & :sp filename
打开一个新窗口,如果有加filename,表示在新窗口打开一个文件,否则表示两个窗口为同 &
& &一个文件内容(同步显示)。
& & ctrl+w+j
按键的按法:先按下ctrl不放,在按下w后放开所有的按键,然后在按下j(或下下箭头键), &
& &则光标可移动到下方的窗口。
& & ctrl+w+k
同上,不过光标移动到上面的窗口。
& & ctrl+w+q
其实就是:q结束离开。举例来说,如果我想要结束下方的窗口,那么利用ctrl+w+[向下键],然 &
&后:q即可离开,也可以ctrl+w+q
15、vim的环境设置可以写入~/.vimrc文件中。
16、可以使用iconv进行文件语系编码的转换。
& & [root@www ~]#
iconv --list
& & [root@www ~]#
iconv -f 原本编码 -t 新编码 filename [-o newfile]
& & 选项与参数:
& & --list :列出
iconv 支持的语系数据
& & :from
,亦即来源之意,后接原本的编码格式;
& & :to ,亦即后来的新编码要是什么格式;
file:如果要保留原本的档案,那么使用 -o 新档名,可以建立新编码档案。
& & eg:iconv -f
big5 -t utf8 vi.big5 -o vi.utf8
17、使用dos2UNIX及UNIX2dos可以变更文件每一行的行为断行字符。
& & [root@www ~]#
dos2unix [-kn] file [newfile]
& & [root@www ~]#
unix2dos [-kn] file [newfile]
& & 选项与参数:
&:保留该档案原本的 mtime 时间格式 (不更新档案上次内容经过修订的时间)
&:保留原本的旧档,将转换后的内容输出到新档案,如: dos2unix -n old
vim常用指令
移动光标的方法:
Ctrl+f:屏幕向下移动一页。
Ctrl+b:屏幕向上移动一页。
0或[Home]:移到这一行最前面字符。
$或[End]:移动到这一行最后面字符。
G:移动到这个文件的最后一行。
nG:移动到第N行
gg:移动到这个文件的第一行,相当于1G。
N[Enter]:n为数字。光标向下移动n行。
查找与替换
向下寻找一个名称为Word的字符串。例如要在文件内查找bbird这个字符串,就输入/vbird即 &
:na,n2s/word1/word2/g
n1,n2为数字。在n1与n2行之间寻找word1这个字符串,并将该字符串替换为word2
:1,$s/word1/word2/g &
从第一行到最后一行查找word1字符串,并将该字符串替换为word2。
:1,$s/word1/word2/gc
&从第一行到最后一行查找word1字符串,并将该字符串替换为word2,且在替换前显示提
示字符给用户确认(confirm)是否需要替换。
删除、复制与粘贴
在一行字当中,x为向后删除一个字符(相当于del按键),X为向前删除一个字符(相当于backspace)
ndd n & 为数字。删除光标所在的那一行
复制光标所在向下n行。
p,P & &粘贴
& 复原前一个操作
Ctrl+r &重做上一个操作
第11章 认识与学习bash
1、由于内核在内存中是受保护的块,因此我们必须要通过"Shell"将我们输入的命令与Kernel通信,好让Kernel
& &可以控制硬件来正确无误地工作。
& &bash 和C
2、学习shell的原因主要有:命令行界面的shell在各大distribution都一样;远程管理时命令行界面速度较
&快;shell是管理Linux系统非常重要的一环,因为Linux内很多控制都是以shell编写的。
3、系统合法的shell均写在/etc/shells文件中。
4、用户默认登录取得的shell记录于/etc/passwd的最后一个字段。
5、bash的功能主要有命令编辑功能、命令与文件补全功能、命令别名设置功能、作业控制、前台、后台控制、
& &程序化脚本、通配符。
6、type可以用来找到执行命令为何种类型,也可用于which相同的功能。
& &[root@www ~]#
type [-tpa] name
& &选项与参数:
& &:不加任何选项与参数时,type 会显示出 name
是外部命令还是 bash 内建命令
&:当加入 -t 参数时,type 会将 name 以底下这些字眼显示出他的意义:
& &:表示为外部命令;
& :表示该命令为命令别名所配置的名称;
& &builtin :表示该命令为
bash 内建的命令功能;
&:如果后面接的 name 为外部命令时,才会显示完整文件名;
&:会由 PATH 变量定义的路径中,将所有含 name 的命令都列出来,包含
7、变量就是以一组文字或符号等来替换一些设置或者是一串保留的数据。
& &eg:echo
& &unset 变量名称
& &cd /lib/modules/$(uname
-r)/kernel
8、变量主要有环境变量与自定义变量,或称为全局变量与局部变量。
9、使用env与export可查看环境变量,其中export可以将自定义变量转成环境变量。
& &eg:env
10、set可以查看目前bash环境下的所有变量。
11、$?也为变量,是前一个命令执行完毕后的回传吗。在Linux回传码为0代表执行成功。
12、locale可用于查看语系数据。
13、可用read让用户由键盘输入变量得值。
& & [root@www ~]#
read [-pt] variable
&-p &:后面可以接提示字符!
&:后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦!
14、ulimit可用以限制用户使用系统的资源情况。
& & [root@www ~]#
ulimit [-SHacdfltu] [配额]
& & 选项与参数:
&:hard limit ,严格的配置,必定不能超过这个配置的数值;
&:soft limit ,警告的配置,可以超过这个配置值,但是若超过则有警告信息。
& & & 在配置上,通常
soft 会比 hard 小,举例来说,soft 可配置为 80 而 hard&
& & & 配置为
100,那么你可以使用到 90 (因为没有超过 100),但介于 80~100 之间时,
系统会有警告信息通知你!
&:后面不接任何选项与参数,可列出所有的限制额度;
&:当某些程序发生错误时,系统可能会将该程序在内存中的信息写成文件(除错用),
这种文件就被称为核心文件(core file)。此为限制每个核心文件的最大容量。
&:此 shell 可以创建的最大文件容量(一般可能配置为 2GB)单位为 Kbytes
&:程序可使用的最大断裂内存(segment)容量;
&:可用于锁定 (lock) 的内存量
&:可使用的最大 CPU 时间 (单位为秒)
&:单一用户可以使用的最大程序(process)数量。
& & [root@www ~]#
history [n]
& & [root@www ~]#
history [-c]
& & [root@www ~]#
history [-raw] histfiles
& & 选项与参数:
& :数字,意思是『要列出最近的 n 笔命令行表』的意思!
&:将目前的 shell 中的所有 history 内容全部消除
&:将目前新增的 history 命令新增入 histfiles 中,若没有加 histfiles
& & & 则默认写入
~/.bash_history
&:将 histfiles 的内容读到目前这个 shell 的 history 记忆中;
&:将目前的 history 记忆内容写入 histfiles 中!
15、bash的配置文件主要分为loginshell与non-login shell。login
shell主要 & & &
读取/etc/profile~/.bash_profile,non-login则仅读取~/.bashrc。
& & cat /etc/issue
登录与欢迎信息
& & source 配置文件名
读入环境配置文件的命令
16、通配符主要有*、?、[]等。
17、数据流重定向通过&、2&、&之类的符号将输出的信息转到其他文件或设备去。
1)标准输入(stdin):代码0,使用&或&&;
2)标准输出(stdout):代码1,使用&或&&;
3)标准错误输出(stderr):代码为2,使用2&或2&&。
&&是覆盖,&&是累加
&eg:将命令的数据全部写入名为list的文件中
& & &find /home
-name .bashrc &list
& & &find /home
-name .bashrc 2& /dev/null
& catfile & ~/.bashrc
18、连续命令的执行可通过;&&、||等符号来处理。
& &;不考虑相关性
&& cmd2 &1. 若 cmd1
运行完毕且正确运行($?=0),则开始运行 cmd2。
& 2. 若 cmd1 运行完毕且为错误 ($?≠0),则 cmd2 不运行。
& & cmd1 || cmd2
&1. 若 cmd1 运行完毕且正确运行($?=0),则 cmd2 不运行。
& 2. 若 cmd1 运行完毕且为错误 ($?≠0),则开始运行 cmd2。
19、管道命令的重点是它仅会处理standard output,对于standard error
output会予以忽略。管道命令必须要 & &
能够接受来自前一个命令的数据称为standar input继续处理才行。
20、本章介绍的管道命令主要有cut,grep,sort,wc,uniq,tee,tr,col,join,paste,expand,split,
选取命令:cut grep
[root@www ~]#&cut -d'分隔字符' -f fields
&==用于有特定分隔字符
[root@www ~]#&cut -c 字符区间
&&==用于排列整齐的信息
选项与参数:
-d &:后面接分隔字符。与 -f 一起使用;
-f &:依据 -d 的分隔字符将一段信息分割成为数段,用 -f
取出第几段的意思;
-c &:以字符 (characters)
的单位取出固定字符区间;
[root@www ~]#&grep [-acinv]
[--color=auto] '搜寻字符串' filename
选项与参数:
-a :将 binary 文件以 text 文件的方式搜寻数据
-c :计算找到 '搜寻字符串' 的次数
-i :忽略大小写的不同,所以大小写视为相同
-n :顺便输出行号
-v :反向选择,亦即显示出没有 '搜寻字符串' 内容的那一行!
--color=auto :可以将找到的关键词部分加上颜色的显示喔!
排序命令: sort, wc, uniq
[root@www ~]#&sort [-fbMnrtuk] [file
选项与参数:
-f &:忽略大小写的差异,例如 A 与 a 视为编码相同;
-b &:忽略最前面的空格符部分;
-M &:以月份的名字来排序,例如 JAN, DEC
等等的排序方法;
&:使用『纯数字』进行排序(默认是以文字型态来排序的);
-r &:反向排序;
-u &:就是 uniq ,相同的数据中,仅出现一行代表;
-t &:分隔符,默认是用 [tab] 键来分隔;
-k &:以那个区间 (field) 来进行排序的意思
[root@www ~]#&uniq [-ic]
选项与参数:
-i &:忽略大小写字符的不同;
-c &:进行计数
[root@www ~]#&wc [-lwm]
选项与参数:
-l &:仅列出行;
-w &:仅列出多少字(英文单字);
-m &:多少字符;
双向重导向: tee
[root@www ~]# ls -l / | tee -a ~/homefile |
# 要注意! tee 后接的文件会被覆盖,若加上 -a 这个选项则能将信息累加。
字符转换命令: tr, col, join, paste, expand
分割命令: split
[root@www ~]#&split [-bl] file
选项与参数:
-b &:后面可接欲分割成的文件大小,可加单位,例如 b, k, m
-l &:以行数来进行分割。
PREFIX :代表前导符的意思,可作为分割文件的前导文字。
eg:cd / split -b 300k /etc/termcap termcap
关于减号 - 的用途
eg:tar -cvf - /home | tar -xvf -
上面这个例子是说:『我将 /home 里面的文件给他打包,但打包的数据不是纪录到文件,而是传送到
stdout; 经过管线后,将 tar -cvf - /home 传送给后面的 tar -xvf - 』。后面的这个 -
则是取用前一个命令的 stdout, 因此,我们就不需要使用 file 了!这是很常见的例子喔!注意注意!
第12章 正则表达式与文件格式化处理
1、正则表达式就是处理字符串的方法,它是以行为单位来进行字符串的处理行为。
2、正则表达式通过一些特殊符号的辅助,可以让用户轻易达到查找,删除,替换某特定字符串的处理程序。
3、只要工具程序支持正则表达式,那么该工具程序就可以用来作为正则表达式的字符串处理之用。
4、正则表达式与通配符是完全不一样的。通配符(wildcard)代表的是bash操作接口的一个功能,但正则表达式
& &则是一种字符串处理的表示方式!
5、使用grep或其他工具进行正则表达式的字符串比较时,因为编码的问题会有不同的状态,因此,你最好将
&LANG等变量设置为C或者是en等英文语系!
6、grep与egrep在正则表达式里面是常见的两个程序,其中,egrep支持更严谨的正则表达式的语法。
7、由于编码系统的不同,不同的语系(LANG)会造成正则表达式选取数据的去表,因此可利用特殊符号如
& &[:upper:]来替代编码范围较佳。
8、由于严谨度的不同,正则表达式之上还有更严谨的扩展正则表达式。
9、基础正则表达式的特殊字符有*,?,[],[^],^,$等!
基础正则表达式字符
意义:待查找的字符串word在行首 &&
& eg范例: &grep -n
'^#' regular_express.txt & 搜寻行首为 # 开始的那一行!
待搜寻的字符串(word)在行尾!
& & 代表『任意一个』字符,一定是一个任意字符!
& & 重复零个或多个的前一个 RE 字符
\{n,m\} & 连续 n 到 m 个的『前一个 RE
& & & 若为 \{n\}
则是连续 n 个的前一个 RE 字符,
& & & 若是 \{n,\}
则是连续 n 个以上的前一个 RE 字符!
& &字符集合的 RE 特殊字符的符号
意义:从字符集和的RE字符里面找出不要的字符串或范围。
10、常见的正则表达式工具有grep,sed,vim等。
[root@test root]# grep [-acinv] '搜寻字符串'
参数说明:
-a :将 binary 档案以 text 档案的方式搜寻数据
-c :计算找到 '搜寻字符串' 的次数
-i :忽略大小写的不同,所以大小写视为相同
-n :顺便输出行号
-v :反向选择,亦即显示出没有 '搜寻字符串' 内容的那一行!
[root@linux ~]# sed [-nefr] [动作]
-n &:使用安静(silent)模式。在一般 sed
的用法中,所有来自 STDIN
& 的数据一般都会被列出到屏幕上。但如果加上 -n 参数后,则只有经过
& sed 特殊处理的那一行(或者动作)才会被列出来。
-e &:直接在指令列模式上进行 sed 的动作编辑;
-f &:直接将 sed 的动作写在一个档案内, -f filename
则可以执行 filename 内的
& sed 动作;
的动作支持的是延伸型正则表达式的语法。(预设是基础正则表达式语法)
-i &:直接修改读取的档案内容,而不是由屏幕输出。
动作说明: &[n1[,n2]]function
n1, n2 :不见得会存在,一般代表『选择进行动作的行数』,举例来说,如果我的动作
& & &是需要在 10 到
20 行之间进行的,则『 10,20[动作行为] 』
function 有底下这些咚咚:
a & :新增, a
的后面可以接字符串,而这些字符串会在新的一行出现(目前的下一行)~
c & :取代, c 的后面可以接字符串,这些字符串可以取代 n1,n2
之间的行!
d & :删除,因为是删除啊,所以 d
后面通常不接任何咚咚;
i & :插入, i
的后面可以接字符串,而这些字符串会在新的一行出现(目前的上一行);
p & :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed
-n 一起运作~
s & :取代,可以直接进行取代的工作哩!通常这个 s
的动作可以搭配
& 正则表达式!例如 1,20s/old/new/g 就是啦!
范例一:将 /etc/passwd 的内容列出,并且我需要打印行号,同时,请将第 2~5
[root@linux ~]# nl /etc/passwd | sed '2,5d'
范例六:我们可以使用 ifconfig 来列出 IP ,若仅要 eth0 的 IP 时?
[root@linux ~]# ifconfig eth0 | grep 'inet ' | sed
's/^.*addr://g' | \
& sed 's/Bcast.*$//g'
11、printf可以通过一些特殊符号来将数据进行格式化输出。
12、awk可以使用“字段”为依据,进行数据的重新整理与输出。
相较于 sed 常常作用于一整个行的处理, awk 则比较倾向于一行当中分成数个『字段』来处理
[root@linux ~]# awk '条件类型1{动作1} 条件类型2{动作2} ...'
13、文件的比较中,可利用diff及cmp进行比较,其中diff主要用在纯文本文件方面的新旧版本比较。
[root@linux ~]# diff [-bBi] from-file to-file
from-file :一个档名,作为原始比对档案的档名;
to-file & :一个档名,作为目的比对档案的档名;
注意,from-file 或 to-file 可以 - 取代,那个 - 代表『Standard
input』之意。
-b &:忽略一行当中,仅有多个空白的差异(例如 "about me"
与 "about & & me" 视为相同
-B &:忽略空白行的差异。
-i &:忽略大小写的不同。
cmp主要利用『位』单位去比对
14、patch命令可以将旧版数据更新到新版(主要由diff创建patch的补丁来源文件)。
[root@linux ~]# mkdir /tmp/ cp /etc/passwd
[root@linux ~]# mkdir /tmp/ cp /tmp/test/passwd
[root@linux ~]# cd / diff -Naur old/ new/
& test.patch
[root@linux ~]# patch -pN &
patch_file
-p &:后面可以接『取消几层目录』的意思。
范例一:将刚刚制作出来的 patch file 用来更新旧版数据
[root@linux ~]# cd /tmp/old
[root@linux ~]# patch -p1 &
/tmp/test.patch
patching file passwd
# 为什么这里会使用 -p1 呢?因为我们在比对新旧版的数据时,是在 /tmp 底下,
# 而实际的数据是在 /tmp/old 里面,因此,当我们进入到 /tmp/old 时,
# 再查阅 /tmp/test.patch 的第一行如下:
# diff -Naur old/passwd new/passwd (用 head -n 1
/tmp/test.patch)
# 发现到,我们所在的目录其实是 old 里面,所以,就必须要减去一层目录。
script是利用shell的功能所写的一个“程序”(program),这个程序是使用纯文本文件,将一些shell
&的语法与命令(含外部命令)卸载里面,搭配正则表达式、管道命令与数据流重定向等功能,以达到我们所
& &想要的处理目的。
2、shell script用在系统管理上面是很好的一项工具,但是用在处理大量数值运算上九不够好了,因为shell
&script的速度较慢,且使用的CPU资源较多,造成主机资源的分配不良。
3、在shell script的文件中,命令是从上而下、从左而右地分析与执行。
4、shell script的执行至少需要有r的权限,若需要直接命令执行,则需要拥有r与x的权限。
5、在良好的程序编写习惯中,第一行要声明shell(#!/bin/bash),第二行以后则声明程序用途,版本,作者
6、对谈式脚本可用read命令达成。要创建每次执行脚本都有不同结果的数据,可使用date命令利用日期完成。
7、script的执行若以source来执行时,代表在父进程的bash内执行之意!
8、若需要进行半段是,可使用test或中括号([])来处理。
9、在script内,$0,$1,$2,$@,$#是由特殊意义的!
& &$0:是文件名
& &$#:代表后接的参数“个数”
& &$@:全部变量
10、条件判断式可使用if...then来判断,若是固定变量内容的情况下,可使用case$var in esac
11、循环主要分为不定循环(while,until)以及固定循环(for),配合do、done来达成所需任务!
12、我们可使用sh -x scipt.sh来进行程序的调试。
进行运算var=$((运算内容))
判断式[ "$HOME" == "$MAIL" ]
eg:#!/bin/bash
# Program:
This program shows the user's choice
# History:
& First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input (Y/N): " yn
if [ "$yn" == "Y" ] || [ "$yn" == "y" ]; then
echo "OK, continue"
elif [ "$yn" == "N" ] || [ "$yn" == "n" ]; then
echo "Oh, interrupt!"
echo "I don't know what your choice is"
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。Linux下编译程序 (读鸟哥的Linux私房菜笔记) - 潜行怪的专栏
- 博客频道 - CSDN.NET
1416人阅读
在討論程式碼是什麼之前,我們先來談論一下什麼是可執行檔?我們說過,在Linux 系統上面,一個檔案能不能被執行看的是有沒有可執行的那個權限 (具有x permission),不過,Linux 系統上真正認識的可執行檔其實是二進位檔案 (binary program),例如 /usr/bin/passwd, /bin/touch 這些個檔案即為二進位程式碼。
或許你會說 shell scripts 不是也可以執行嗎?其實 shell scripts 只是利用 shell (例如 bash) 這支程式的功能進行一些判斷式,而最終執行的除了 bash 提供的功能外,仍是呼叫一些已經編譯好的二進位程式來執行的呢!當然啦, bash 本身也是一支二進位程式啊!那麼我怎麼知道一個檔案是否為 binary 呢?還記得我們在 這個指令的功能嗎?對啦!用他就是了!我們現在來測試一下:
# 先以系統的檔案測試看看:
[root@www ~]# file /bin/bash
/bin/bash: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/
Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped
# 如果是系統提供的 /etc/init.d/syslog 呢?
[root@www ~]# file /etc/init.d/syslog
/etc/init.d/syslog: Bourne-Again shell script text executable
看到了吧!如果是 binary 而且是可以執行的時候,他就會顯示執行檔類別 (ELF 32-bit LSB executable),同時會說明是否使用動態函式庫 (shared libs),而如果是一般的 script ,那他就會顯示出 text executables 之類的字樣!
既然 Linux 作業系統真正認識的其實是 binary program,那麼我們是如何做出這樣的一支binary 的程式呢?首先,我們必須要寫程式,用什麼東西寫程式?就是一般的文書處理器啊!鳥哥都喜歡使用 來進行程式的撰寫,寫完的程式就是所謂的原始程式碼囉!這個程式碼檔案其實就是一般的純文字檔。在完成這個原始碼檔案的編寫之後,再來就是要將這個檔案『編譯』成為作業系統看的懂得binary
program 囉!而要編譯自然就需要『編譯器』來動作,經過編譯器的編譯與連結之後,就會產生一支可以執行的 binary program 囉。
舉個例子來說,在 Linux 上面最標準的程式語言為 C ,所以我使用 C 的語法進行原始程式碼的書寫,寫完之後,以Linux 上標準的 C 語言編譯器
gcc 這支程式來編譯,就可以製作一支可以執行的binary program 囉。整個的流程有點像這樣:
圖 1.1.1、利用 gcc 編譯器進行程式的編譯流程示意圖&
事實上,在編譯的過程當中還會產生所謂的目標檔 (Object file),這些檔案是以 *.o 的副檔名樣式存在的!至於 C 語言的原始碼檔案通常以 *.c 作為副檔名。此外,有的時候,我們會在程式當中『引用、呼叫』其他的外部副程式,或者是利用其他軟體提供的『函數功能』,這個時候,我們就必須要在編譯的過程當中,將該函式庫給他加進去,如此一來,編譯器就可以將所有的程式碼與函式庫作一個連結 (Link) 以產生正確的執行檔囉。
總之,我們可以這麼說:
開放源碼:就是程式碼,寫給人類看的程式語言,但機器並不認識,所以無法執行;編譯器:將程式碼轉譯成為機器看的懂得語言,就類似翻譯者的角色;可執行檔:經過編譯器變成二進位程式後,機器看的懂所以可以執行的檔案。
在我的ubantu 11.04上编译代码是
首先新建源码
vim hello.c
然后在vim里面写源码,这个与windows下写c源码无差异。
然后再编译这个源码
gcc -o hello hello.c
这个的意思是用gcc这个编译器把hello.c源码编译成hello这个可执行文件,存在当下目录
这是是在当下目录运行hello这个binary program
就OK啦。一个最简单的例子。
在前一小節的示意圖中,在編譯的過程裡面有提到函式庫這東西。什麼是函式庫呢?先舉個例子來說:我們的 Linux 系統上通常已經提供一個可以進行身份驗證的模組,就是在。這個PAM 提供的功能可以讓很多的程式在被執行的時候,除了可以驗證使用者登入的資訊外,還可以將身份確認的資料記錄在裡面,以方便系統管理員的追蹤!
既然有這麼好用的功能,那如果我要編寫具有身份認證功能的程式時,直接引用該 PAM 的功能就好啦,如此一來,我就不需要重新設計認證機制囉!也就是說,只要在我寫的程式碼裡面,設定去呼叫 PAM 的函式功能,我的程式就可以利用 Linux 原本就有的身份認證的程序咯!除此之外,其實我們的 Linux 核心也提供了相當多的函式庫來給硬體開發者利用喔。
函式庫又分為動態與靜態函式庫,這兩個咚咚的分別我們在後面的小節再加以說明。這裡我們以一個簡單的流程圖,來示意一支有呼叫外部函式庫的程式的執行情況。
圖 1.2.1、程式執行時引用外部動態函式庫的示意圖
很簡單的示意圖啊!^_^!而如果要在程式裡面加入引用的函式庫,就需要如 所示,亦即在編譯的過程當中,就需要加入函式庫的相關設定囉。事實上, Linux 的核心提供很多的核心相關函式庫與外部參數,這些核心功能在設計硬體的驅動程式的時候是相當有用的資訊,這些核心相關資訊大多放置在/usr/include,
/lib, /usr/lib裡面哩!我們在本章的後續小節再來探討。反正我們可以簡單的這麼想:
函式庫:就類似副程式的角色,可以被呼叫來執行的一段功能函數。
事實上,使用類似 gcc 的編譯器來進行編譯的過程並不簡單,因為一套軟體並不會僅有一支程式,而是有一堆程式碼檔案。所以除了每個主程式與副程式均需要寫上一筆編譯過程的指令外,還需要寫上最終的連結程序。程式碼小的時候還好,如果是類似 WWW 伺服器軟體 (例如),或者是類似核心的原始碼,動則數百 MBytes 的資料量,編譯指令會寫到瘋掉~這個時候,我們就可以使用make
這個指令的相關功能來進行編譯過程的指令簡化了!
當執行 make 時,make 會在當時的目錄下搜尋 Makefile (or makefile) 這個文字檔,而 Makefile 裡面則記錄了原始碼如何編譯的詳細資訊!make 會自動的判別原始碼是否經過變動了,而自動更新執行檔,是軟體工程師相當好用的一個輔助工具呢!
咦!make 是一支程式,會去找 Makefile ,那 Makefile 怎麼寫?通常軟體開發商都會寫一支偵測程式來偵測使用者的作業環境,以及該作業環境是否有軟體開發商所需要的其他功能,該偵測程式偵測完畢後,就會主動的建立這個Makefile 的規則檔案啦!通常這支偵測程式的檔名為 configure 或者是 config 。
咦!那為什麼要偵測作業環境呢?在當中,不是曾經提過其實每個 Linux distribution 都使用同樣的核心嗎?但妳得要注意,不同版本的核心所使用的系統呼叫可能不相同,而且每個軟體所需要的相依的函式庫也不相同,同時,軟體開發商不會僅針對 Linux 開發,而是會針對整個 Unix-Like 做開發啊!所以他也必須要偵測該作業系統平台有沒有提供合適的編譯器才行!所以當然要偵測環境啊!一般來說,偵測程式會偵測的資料大約有底下這些:
是否有適合的編譯器可以編譯本軟體的程式碼;是否已經存在本軟體所需要的函式庫,或其他需要的相依軟體;作業系統平台是否適合本軟體,包括 Linux 的核心版本;核心的表頭定義檔 (header include) 是否存在 (驅動程式必須要的偵測)。
至於 make 與 configure 運作流程的相關性,我們可以使用底下的圖示來示意一下啊!下圖中,妳要進行的任務其實只有兩個,一個是執行 configure 來建立 Makefile ,這個步驟一定要成功!成功之後再以 make 來呼叫所需要的資料來編譯即可!非常簡單!
圖 1.3.1、透過 configure 與 make 進行編譯示意圖
由於不同的 Linux distribution 的函式庫檔案所放置的路徑,或者是函式庫的檔名訂定,或者是預設安裝的編譯器,以及核心的版本都不相同,因此理論上,妳無法在 CentOS 5.x 上面編譯出 binary program 後,還將他拿到 SuSE 上面執行,這個動作通常是不可能成功的!因為呼叫的目標函式庫位置可能不同 (參考)
,核心版本更不可能相同!所以能夠執行的情況是微乎其微!所以同一套軟體要在不同的平台上面執行時,必須要重複編譯!所以才需要原始碼嘛!瞭解乎!
詳細的 make 用法與 Makefile 規則,在後續的小節裡面再探討囉!
經過上面的介紹之後,你應該比較清楚的知道原始碼、編譯器、函式庫與執行檔之間的相關性了。不過,詳細的流程可能還是不很清楚,所以,在這裡我們以一個簡單的程式範例來說明整個編譯的過程喔!趕緊進入Linux 系統,實地的操作一下底下的範例呢!
單一程式:印出 Hello World
我們以 Linux 上面最常見的 C 語言來撰寫第一支程式!第一支程式最常作的就是.....在螢幕上面印出『Hello World!』的字樣~當然,這裡我們是以簡單的 C 語言來撰寫,如果你對於 C 有興趣的話,那麼請自行購買相關的書籍喔!^_^ 好了,不囉唆,立刻編輯第一支程式吧!
編輯程式碼,亦即原始碼
[root@www ~]# vim hello.c
&==用 C 語言寫的程式副檔名建議用 .c
#include &stdio.h&
int main(void)
printf(&Hello World\n&);
開始編譯與測試執行
[root@www ~]# gcc hello.c
[root@www ~]# ll hello.c a.out
-rwxr-xr-x 1 root root 4725 Jun
5 02:41 a.out
&==此時會產生這個檔名
-rw-r--r-- 1 root root
5 02:40 hello.c
[root@www ~]# ./a.out
Hello World
&==呵呵!成果出現了!
在預設的狀態下,如果我們直接以 gcc 編譯原始碼,並且沒有加上任何參數,則執行檔的檔名會被自動設定為 a.out 這個檔案名稱!所以妳就能夠直接執行 ./a.out 這個執行檔啦!上面的例子很簡單吧!那個 hello.c 就是原始碼,而gcc 就是編譯器,至於 a.out 就是編譯成功的可執行 binary program 囉!咦!那如果我想要產生目標檔 (object file) 來進行其他的動作,而且執行檔的檔名也不要用預設的a.out
,那該如何是好?其實妳可以將上面的第 2 個步驟改成這樣:
[root@www ~]# gcc -c hello.c
[root@www ~]# ll hello*
-rw-r--r-- 1 root root
5 02:40 hello.c
-rw-r--r-- 1 root root 868 Jun
5 02:44 hello.o
&==就是被產生的目標檔
[root@www ~]# gcc -o hello hello.o
[root@www ~]# ll hello*
-rwxr-xr-x 1 root root 4725 Jun
5 02:47 hello
&==這就是可執行檔! -o 的結果
-rw-r--r-- 1 root root
5 02:40 hello.c
-rw-r--r-- 1 root root
5 02:44 hello.o
[root@www ~]# ./hello
Hello World
這個步驟主要是利用 hello.o 這個目標檔製作出一個名為 hello 的執行檔,詳細的 gcc 語法我們會在後續章節中繼續介紹!透過這個動作後,我們可以得到 hello 及 hello.o 兩個檔案,真正可以執行的是 hello 這個 binary program 喔!或許你會覺得,咦!只要一個動作作出 a.out 就好了,幹嘛還要先製作目標檔再做成執行檔呢?呵呵!透過下個範例,你就可以知道為什麼啦!
如果我們在一個主程式裡面又呼叫了另一個副程式呢?這是很常見的一個程式寫法,因為可以簡化整個程式的易讀性!在底下的例子當中,我們以thanks.c 這個主程式去呼叫 thanks_2.c 這個副程式,寫法很簡單:
撰寫所需要的主、副程式
# 1. 編輯主程式:
[root@www ~]# vim thanks.c
#include &stdio.h&
int main(void)
printf(&Hello World\n&);
thanks_2();
# 上面的 thanks_2(); 那一行就是呼叫副程式啦!
[root@www ~]# vim thanks_2.c
#include &stdio.h&
void thanks_2(void)
printf(&Thank you!\n&);
進行程式的編譯與連結 (Link)
# 2. 開始將原始碼編譯成為可執行的 binary file :
[root@www ~]# gcc -c thanks.c thanks_2.c
[root@www ~]# ll thanks*
-rw-r--r-- 1 root root
5 16:13 thanks_2.c
-rw-r--r-- 1 root root 856 Jun
5 16:13 thanks_2.o
&==編譯產生的!
-rw-r--r-- 1 root root
5 16:11 thanks.c
-rw-r--r-- 1 root root 908 Jun
5 16:13 thanks.o
&==編譯產生的!
[root@www ~]# gcc -o thanks thanks.o thanks_2.o
[root@www ~]# ll thanks*
-rwxr-xr-x 1 root root 4870 Jun
5 16:17 thanks
&==最終結果會產生這玩意兒
# 3. 執行一下這個檔案:
[root@www ~]# ./thanks
Hello World
Thank you!
知道為什麼要製作出目標檔了嗎?由於我們的原始碼檔案有時並非僅只有一個檔案,所以我們無法直接進行編譯。這個時候就需要先產生目標檔,然後再以連結製作成為 binary 可執行檔。另外,如果有一天,你更新了 thanks_2.c 這個檔案的內容,則你只要重新編譯 thanks_2.c 來產生新的 thanks_2.o,然後再以連結製作出新的 binary 可執行檔即可!而不必重新編譯其他沒有更動過的原始碼檔案。這對於軟體開發者來說,是一個很重要的功能,因為有時候要將偌大的原始碼全部編譯完成,會花很長的一段時間呢!
此外,如果你想要讓程式在執行的時候具有比較好的效能,或者是其他的除錯功能時,可以在編譯的過程裡面加入適當的參數,例如底下的例子:
[root@www ~]# gcc -O -c thanks.c thanks_2.c
&== -O 為產生最佳化的參數
[root@www ~]# gcc -Wall -c thanks.c thanks_2.c
thanks.c: In function 'main':
thanks.c:5: warning: implicit declaration of function 'thanks_2'
thanks.c:6: warning: control reaches end of non-void function
# -Wall 為產生更詳細的編譯過程資訊。上面的訊息為警告訊息 (warning)
# 所以不用理會也沒有關係!
至於更多的 gcc 額外參數功能,就得要 man gcc 囉~呵呵!可多的跟天書一樣~
呼叫外部函式庫:加入連結的函式庫
剛剛我們都僅只是在螢幕上面印出一些字眼而已,如果說要計算數學公式呢?例如我們想要計算出三角函數裡面的sin (90度角)。要注意的是,大多數的程式語言都是使用徑度而不是一般我們在計算的『角度』,180 度角約等於 3.14 徑度!嗯!那我們就來寫一下這個程式吧!
[root@www ~]# vim sin.c
#include &stdio.h&
int main(void)
value = sin ( 3.14 / 2 );
printf(&%f\n&,value);
那要如何編譯這支程式呢?我們先直接編譯看看:
[root@www ~]# gcc sin.c
sin.c: In function 'main':
sin.c:5: warning: incompatible implicit declaration of built-in function 'sin'
/tmp/ccsfvijY.o: In function `main':
sin.c:(.text+0x1b): undefined reference to `sin'
collect2: ld returned 1 exit status
# 注意看到上面最後一行,會有個錯誤訊息,代表沒有成功!
特別注意上面的錯誤訊息,唉啊!怎麼沒有編譯成功?它說的是『undefinedreference to sin』,說的是『沒有 sin 的相關定義參考值!』,為什麼會這樣呢?這是因為 C 語言裡面的 sin 函示是寫在 libm.so 這個函式庫中,而我們並沒有在原始碼裡面將這個函式庫功能加進去,所以當然就需要在編譯與連結的時候將這個函式庫給他連結進執行檔裡面啊!我們可以這樣做:
編譯時加入額外函式庫連結的方式:
[root@www ~]# gcc sin.c -lm -L/lib -L/usr/lib
&==重點在 -lm
[root@www ~]# ./a.out
&==嘗試執行新檔案!
特別注意,使用 gcc 編譯時所加入的那個 -lm 是有意義的,他可以拆開成兩部份來看:
-l :是『加入某個函式庫(library)』的意思,&m :則是 libm.so 這個函式庫,其中, lib 與副檔名(.a 或 .so)不需要寫
所以 -lm 表示使用 libm.so (或 libm.a) 這個函式庫的意思~至於那個 -L 後面接的路徑呢?這表示:『我要的函式庫 libm.so 請到 /lib 或 /usr/lib 裡面搜尋!』
上面的說明很清楚了吧!不過,要注意的是,由於 Linux 預設是將函式庫放置在 /lib 與 /usr/lib 當中,所以你沒有寫 -L/lib 與 -L/usr/lib 也沒有關係的!不過,萬一哪天你使用的函式庫並非放置在這兩個目錄下,那麼-L/path 就很重要了!否則會找不到函式庫喔!
除了連結的函式庫之外,你或許已經發現一個奇怪的地方,那就是在我們的sin.c 當中第一行『 #include&stdio.h&』,這行說的是要將一些定義資料由 stdio.h 這個檔案讀入,這包括 printf 的相關設定。這個檔案其實是放置在 /usr/include/stdio.h的!那麼萬一這個檔案並非放置在這裡呢?那麼我們就可以使用底下的方式來定義出要讀取的include 檔案放置的目錄:
[root@www ~]# gcc sin.c -lm -I/usr/include
-I/path 後面接的路徑( Path )就是設定要去搜尋相關的include 檔案的目錄啦!不過,同樣的,預設值是放置在 /usr/include 底下,除非你的include 檔案放置在其他路徑,否則也可以略過這個項目!
透過上面的幾個小範例,你應該對於 gcc 以及原始碼有一定程度的認識了,再接下來,我們來稍微整理一下gcc 的簡易使用方法吧!
gcc 的簡易用法 (編譯、參數與鏈結)
前面說過, gcc 為 Linux 上面最標準的編譯器,這個 gcc 是由 所維護的,有興趣的朋友請自行前往參考。既然 gcc 對於 Linux 上的 Open source是這麼樣的重要,所以底下我們就列舉幾個 gcc 常見的參數,如此一來大家應該更容易瞭解原始碼的各項功能吧!
# 僅將原始碼編譯成為目標檔,並不製作連結等功能:
[root@www ~]# gcc -c hello.c
# 會自動的產生 hello.o 這個檔案,但是並不會產生 binary 執行檔。
# 在編譯的時候,依據作業環境給予最佳化執行速度
[root@www ~]# gcc -O hello.c -c
# 會自動的產生 hello.o 這個檔案,並且進行最佳化喔!
# 在進行 binary file 製作時,將連結的函式庫與相關的路徑填入
[root@www ~]# gcc sin.c -lm -L/usr/lib -I/usr/include
# 這個指令較常下達在最終連結成 binary file 的時候,
# -lm 指的是 libm.so 或 libm.a 這個函式庫檔案;
# -L 後面接的路徑是剛剛上面那個函式庫的搜尋目錄;
# -I 後面接的是原始碼內的 include 檔案之所在目錄。
# 將編譯的結果輸出成某個特定檔名
[root@www ~]# gcc -o hello hello.c
# -o 後面接的是要輸出的 binary file 檔名
# 在編譯的時候,輸出較多的訊息說明
[root@www ~]# gcc -o hello hello.c -Wall
# 加入 -Wall 之後,程式的編譯會變的較為嚴謹一點,
# 所以警告訊息也會顯示出來!
比較重要的大概就是這一些。另外,我們通常稱-Wall 或者 -O 這些非必要的參數為旗標 (FLAGS),因為我們使用的是 C 程式語言,所以有時候也會簡稱這些旗標為CFLAGS ,這些變數偶爾會被使用的喔!尤其是在後頭會介紹的 make 相關的用法時,更是重要的很吶! ^_^
用 make 進行巨集編譯
在本章一開始我們提到過 make 的功能是可以簡化編譯過程裡面所下達的指令,同時還具有很多很方便的功能!那麼底下咱們就來試看看使用make 簡化下達編譯指令的流程吧!
為什麼要用 make
先來想像一個案例,假設我的執行檔裡面包含了四個原始碼檔案,分別是 main.c haha.c sin_value.c cos_value.c 這四個檔案,這四個檔案的目的是:
main.c :主要的目的是讓使用者輸入角度資料與呼叫其他三支副程式;haha.c :輸出一堆有的沒有的訊息而已;sin_value.c :計算使用者輸入的角度(360) sin 數值;cos_value.c :計算使用者輸入的角度(360) cos 數值。
這四個檔案你可以到 來下載。由於這四個檔案裡面包含了相關性,並且還用到數學函式在裡面,所以如果你想要讓這個程式可以跑,那麼就需要這樣編譯:
# 1. 先進行目標檔的編譯,最終會有四個 *.o 的檔名出現:
[root@www ~]# gcc -c main.c
[root@www ~]# gcc -c haha.c
[root@www ~]# gcc -c sin_value.c
[root@www ~]# gcc -c cos_value.c
# 2. 再進行連結成為執行檔,並加入 libm 的數學函式,以產生 main 執行檔:
[root@www ~]# gcc -o main main.o haha.o sin_value.o cos_value.o \
& -lm -L/usr/lib -L/lib
# 3. 本程式的執行結果,必須輸入姓名、360 度角的角度值來計算:
[root@www ~]# ./main
Please input your name: VBird
&==這裡先輸入名字
Please enter the degree angle (ex& 90): 30
&==輸入以 360 度角為主的角度
Hi, Dear VBird, nice to meet you.
&==這三行為輸出的結果喔!
The Sin is:
The Cos is:
編譯的過程需要進行好多動作啊!而且如果要重新編譯,則上述的流程得要重新來一遍,光是找出這些指令就夠煩人的了!如果可以的話,能不能一個步驟就給他完成上面所有的動作呢?那就利用 make 這個工具吧!先試看看在這個目錄下建立一個名為 makefile 的檔案,內容如下:
# 1. 先編輯 makefile 這個規則檔,內容只要作出 main 這個執行檔
[root@www ~]# vim makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 注意:第二行的 gcc 之前是 &tab& 按鍵產生的空格喔!
# 2. 嘗試使用 makefile 制訂的規則進行編譯的行為:
[root@www ~]# rm -f main *.o
&==先將之前的目標檔去除
[root@www ~]# make
-c -o main.o main.c
-c -o haha.o haha.c
-c -o sin_value.o sin_value.c
-c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 此時 make 會去讀取 makefile 的內容,並根據內容直接去給他編譯相關的檔案囉!
# 3. 在不刪除任何檔案的情況下,重新執行一次編譯的動作:
[root@www ~]# make
make: `main' is up to date.
# 看到了吧!是否很方便呢!只會進行更新 (update) 的動作而已。
或許你會說:『如果我建立一個 shell script 來將上面的所有動作都集結在一起,不是具有同樣的效果嗎?』呵呵!效果當然不一樣,以上面的測試為例,我們僅寫出 main 需要的目標檔,結果 make 會主動的去判斷每個目標檔相關的原始碼檔案,並直接予以編譯,最後再直接進行連結的動作!真的是很方便啊!此外,如果我們更動過某些原始碼檔案,則 make 也可以主動的判斷哪一個原始碼與相關的目標檔檔案有更新過,並僅更新該檔案,如此一來,將可大大的節省很多編譯的時間呢!要知道,某些程式在進行編譯的行為時,會消耗很多的CPU
資源呢!所以說, make 有這些好處:
簡化編譯時所需要下達的指令;若在編譯完成之後,修改了某個原始碼檔案,則 make 僅會針對被修改了的檔案進行編譯,其他的object file 不會被更動;最後可以依照相依性來更新 (update) 執行檔。
既然 make 有這麼多的優點,那麼我們當然就得好好的瞭解一下 make 這個令人關心的傢伙啦!而 make 裡面最需要注意的大概就是那個規則檔案,也就是 makefile 這個檔案的語法啦!所以底下我們就針對 makefile 的語法來加以介紹囉。
makefile 的基本語法與變數
make 的語法可是相當的多而複雜的,有興趣的話可以到
()去查閱相關的說明,鳥哥這裡僅列出一些基本的規則,重點在於讓讀者們未來在接觸原始碼時,不會太緊張啊!好了,基本的 makefile 規則是這樣的:
標的(target): 目標檔1 目標檔2
gcc -o 欲建立的執行檔 目標檔1 目標檔2
那個標的 (target) 就是我們想要建立的資訊,而目標檔就是具有相關性的 object files ,那建立執行檔的語法就是以 &tab& 按鍵開頭的那一行!特別給他留意喔,『命令列必須要以 tab 按鍵作為開頭』才行!他的規則基本上是這樣的:
在 makefile 當中的 # 代表註解;&tab& 需要在命令行 (例如 gcc 這個編譯器指令) 的第一個字元;標的 (target) 與相依檔案(就是目標檔)之間需以『:』隔開。
同樣的,我們以剛剛上一個小節的範例進一步說明,如果我想要有兩個以上的執行動作時,例如下達一個指令就直接清除掉所有的目標檔與執行檔,該如何製作呢?
# 1. 先編輯 makefile 來建立新的規則,此規則的標的名稱為 clean :
[root@www ~]# vi makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
rm -f main main.o haha.o sin_value.o cos_value.o
# 2. 以新的標的 (clean) 測試看看執行 make 的結果:
[root@www ~]# make clean
&==就是這裡!透過 make 以 clean 為標的
rm -rf main main.o haha.o sin_value.o cos_value.o
如此一來,我們的 makefile 裡面就具有至少兩個標的,分別是 main 與 clean ,如果我們想要建立 main 的話,輸入『make main』,如果想要清除有的沒的,輸入『makeclean』即可啊!而如果想要先清除目標檔再編譯 main 這個程式的話,就可以這樣輸入:『make clean main』,如下所示:
[root@www ~]# make clean main
rm -rf main main.o haha.o sin_value.o cos_value.o
-c -o main.o main.c
-c -o haha.o haha.c
-c -o sin_value.o sin_value.c
-c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
這樣就很清楚了吧!但是,你是否會覺得,咦! makefile 裡面怎麼重複的資料這麼多啊!沒錯!所以我們可以再藉由 shellscript 那時學到的『變數』來更簡化 makefile 喔:
[root@www ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
main: ${OBJS}
gcc -o main ${OBJS} ${LIBS}
rm -f main ${OBJS}
的語法有點不太相同,變數的基本語法為:
變數與變數內容以『=』隔開,同時兩邊可以具有空格;變數左邊不可以有 &tab& ,例如上面範例的第一行 LIBS 左邊不可以是 &tab&;變數與變數內容在『=』兩邊不能具有『:』;在習慣上,變數最好是以『大寫字母』為主;運用變數時,以 ${變數} 或 $(變數) 使用;在該 shell 的環境變數是可以被套用的,例如提到的 CFLAGS 這個變數!在指令列模式也可以給予變數。
由於 gcc 在進行編譯的行為時,會主動的去讀取 CFLAGS這個環境變數,所以,你可以直接在 shell 定義出這個環境變數,也可以在makefile 檔案裡面去定義,更可以在指令列當中給予這個咚咚呢!例如:
[root@www ~]# CFLAGS=&-Wall& make clean main
# 這個動作在上 make 進行編譯時,會去取用 CFLAGS 的變數內容!
也可以這樣:
[root@www ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
gcc -o main ${OBJS} ${LIBS}
rm -f main ${OBJS}
咦!我可以利用指令列進行環境變數的輸入,也可以在檔案內直接指定環境變數,那萬一這個CFLAGS 的內容在指令列與 makefile 裡面並不相同時,以那個方式輸入的為主?呵呵!問了個好問題啊!環境變數取用的規則是這樣的:
make 指令列後面加上的環境變數為優先;makefile 裡面指定的環境變數第二;shell 原本具有的環境變數第三。
此外,還有一些特殊的變數需要瞭解的喔:
$@:代表目前的標的(target)
所以我也可以將 makefile 改成:
[root@www ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
gcc -o $@ ${OBJS} ${LIBS}
&==那個 $@ 就是 main !
rm -f main ${OBJS}
這樣是否稍微瞭解了 makefile (也可能是 Makefile) 的基本語法?這對於你未來自行修改原始碼的編譯規則時,是很有幫助的喔!^_^!
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:113482次
积分:1609
积分:1609
排名:第12274名
原创:47篇
转载:54篇
评论:28条
(1)(2)(1)(1)(3)(2)(3)(27)(5)(2)(1)(2)(3)(3)(7)(6)(7)(2)(8)(4)(7)(4)(2)}

我要回帖

更多关于 鸟哥的私房菜 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信