0%

PGLAB – x轴、y轴以及标题

对应的C函数为

1
void cpglab(const char *xlbl, const char *ylbl, const char *toplbl);

在视口的外面做标签。这个函数是PGMTXT的简易接口。如果PGLAB描述的信息不够充分,还需要使用PGMTXT。

参数:

1
2
3
XLBL   (输入) : x轴的标签(视口底端居中)
YLBL (输入) : y轴的标签(视口左端居中,垂直描述)
TOPLBL (输入) : 整个图像的标签 (视口上方居中)

cpglab

PGAXIS – 绘制一个轴

对应的C函数为

1
void cpgaxis(const char *opt, float x1, float y1, float x2, float y2, float v1, float v2, float step, int nsub, float dmajl, float dmajr, float fmin, float disp, float orient);

绘制一个从点(X1,Y1)到点(X2,Y2)的轴。

正常情况下,这个函数绘制一条平均细分的标准线。

如果选项’L’存在,那么将绘制对数轴。

参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
OPT    (输入)  : 单字符选项:
L : 绘制对数轴
N : 标记数字坐标
1 : 强制十进制坐标
2 : 强制指数坐标
X1, Y1 (输入) : 绘制坐标轴的一点
X2, Y2 (输入) : 绘制坐标轴的另一点
V1 (输入) : 绘制坐标轴的起始值
V2 (输入) : 绘制坐标轴的终止值
STEP (输入) : 绘制坐标轴的主要刻度标记如果STEP=0.0,则有pgplot自动选择
NSUB (输入) : 绘制坐标轴的细分刻度标记,如果NSUB <= 1,则不显示。如果是对数轴,NSUB将被忽略。
DMAJL (输入) : 坐标轴上方刻度标记的长度
DMAJR (输入) : 坐标轴下方刻度标记的长度
FMIN (输入) : 主刻度之间小的刻度标记
DISP (输入) : 底部坐标距离轴的距离
ORIENT (输入) : 标签文本的角度方位 (0-360°).

PGASK – 控制新页的提示

对应的C函数为

1
void cpgask(Logical flag);

改变PGPLOT的提示状态。如果提示状态是开的,那么PGPAGE 将提示``Type RETURN for next page:’’并等待用户输入回车以显示新的页面。初始的提示状态为了交互设备一般都是打开的,而对于非交互设备,提示状态都是关闭的。

参数:

1
2
FLAG   (输入)  : 	如果为1,并且设备为交互式设备提示状态打开。
如果为0,提示状态关闭。

PGARRO – 绘制一个箭头

对应的C函数为

1
void cpgarro(float x1, float y1, float x2, float y2);

从(X1,Y1)到(X2,Y2)绘制一个箭头。

箭头的大小取决于PGSCH的设定。默认大小为视图表面的1/40。 箭头的形状取决于PGSAH。

参数:

1
2
X1, Y1 (输入)  : 箭头尾部世界坐标
X2, Y2 (输入) : 箭头头部世界坐标

递归和迭代

  • 迭代基于循环结构;
  • 递归基于选择结构
  • 迭代是显式地使用一个循环结构
  • 递归通过重复地进行函数调用来实现循环
  • 迭代总是不断地改变计数器变量直至它的值使得循环继续条件为假
  • 递归总是不断地将问题的规模逐渐变小直至到达基线条件
  • 递归有很多负面作用,递归需要不断地执行函数调用机制,因此,产生很大的函数调用开销,从而在处理器的时间和存储器的空间两方面导致很大的开销。由于每次递归调用都要创建函数的一个副本,这是很耗费存储器的。

迭代通常都是发生在一个函数内部,所以反复执行函数调用机构的开销和额外占用的存储器空间基本上可以忽略不计。

linux中关于time.h的函数

对time.h中的若干个获取时间以及时间转换的函数困惑了有一段时间了,每次操作都要man一下,查看一下具体含义,今天把这些总结一下,算是了了一桩心事。

关系图为(其实搞定这个图也就OK了):

time.h是C标准函数库中获取时间与日期、对时间与日期数据操作及格式化的头文件。

表示时间的三种数据类型

  1. 日历时间(calendar time),是从一个标准时间点(epoch)到现在的时间经过的秒数,不包括插入闰秒对时间的调整。开始计时的标准时间点,各种编译器一般使用1970年1月1日0时0秒。日历时间用数据类型time_t表示。time_t类型实际上一般是32位或64位整数类型;
  2. 时钟滴答数(clock tick),从进程启动开始计时,因此这是相对时间。每秒钟包含CLOCKS_PER_SECtime.h中定义的常量,一般为1000)个时钟滴答。时钟滴答数用数据类型clock_t表示。clock_t类型一般是32位整数类型;
  3. 分解时间(broken-down time),用结构数据类型tm表示,tm包含下列结构成员:
成员 描述
int tm_hour hour (0 – 23)
int tm_isdst 夏令时 enabled (> 0), disabled (= 0), or unknown (< 0)
int tm_mday day of the month (1 – 31)
int tm_min minutes (0 – 59)
int tm_mon month (0 – 11, 0 = January)
int tm_sec seconds (0 – 60, 60 = Leap second)
int tm_wday day of the week (0 – 6, 0 = Sunday)
int tm_yday day of the year (0 – 365)
int tm_year year since 1900

从计算机系统时钟获得时间的方法

  • time_t time(time_t* timer):得到从标准计时点(一般是1970年1月1日午夜)到当前时间的秒数
  • clock_t clock(void):得到从进程启动到此次函数调用的累计的时钟滴答数。

三种时间日期数据类型的转换函数

1
2
struct
tm* gmtime(const time_t* timer)

从日历时间time_t到分解时间tm的转换。函数返回的是一个静态分配的tm结构存储空间,该存储空间被gmtime, localtimectime函数所共用. 这些函数的每一次调用会覆盖这块tm结构存储空间的内容。

1
2
struct
tm* gmtime_r(const time_t* timer, struct tm* result)

该函数是gmtime函数的线程安全版本.

1
2
struct
tm* localtime(const time_t* timer)

从日历时间time_t到分解时间tm的转换,即结果数据已经调整到本地时区与夏令时。

1
2
time_t
mktime(struct tm* ptm)

从分解时间tm到日历时间time_t的转换。

1
2
time_t
timegm(struct tm* brokentime)

从分解时间tm(被视作UTC时间,不考虑本地时区设置)到日历时间time_t的转换。该函数较少被使用。

时间日期数据的格式化函数

1
char *asctime(const struct tm* tmptr)

把分解时间tm输出到字符串,结果的格式为“Www Mmm dd hh:mm:ss yyyy”,即“周几月份数日数小时数:分钟数:秒钟数年份数”。函数返回的字符串为静态分配,长度不大于26,与ctime函数共用。函数的每次调用将覆盖该字符串内容。

1
2
char*
ctime(const time_t* timer)

把日历时间time_t timer输出到字符串,输出格式与asctime函数一样.

1
2
3
size_t
strftime(char* s, size_t n, const char* format, const struct tm*
tptr)

把分解时间tm转换为自定义格式的字符串,类似于常见的字符串格式输出函数sprintf

1
2
3
char *
strptime(const char* buf, const char* format, struct tm*
tptr)

strftime的逆操作,把字符串按照自定义的格式转换为分解时间tm

对时间数据的操作

1
2
double
difftime(time_t timer2, time_t timer1)

比较两个日历时间之差。

time – get the Coordinated Universal Time seconds from 1970.1.1

1
2
time_t
time(time_t* timer)

获取当前的系统时间,返回的结果是一个time_t类型,其实就是一个大整数,其值表示从CUT(Coordinated Universal Time)时间1970年1月1日00:00:00(称为UNIX系统的Epoch时间)到当前时刻的秒数。然后调用localtimetime_t所表示的CUT时间转换为本地时间(我们是+8区,比CUT多8个小时)并转成struct tm类型,该类型的各数据成员分别表示年月日时分秒。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <time.h>

int main(void)

{

time_t timer = time(NULL);

printf(“The number of seconds since January 1, 1970 is %ld\n”, timer);

return 0;

}

gettimeofday – get time

获得当前精确时间(UNIX到现在的时间)。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<stdio.h>
#include<sys/time.h>

#include<unistd.h>

int main()

{

struct timeval tv;

struct timezone tz;

gettimeofday(&tv, &tz);

printf(“tv_sec:%ld\n”, tv.tv_sec);

printf(“tv_usec:%ld\n”, tv.tv_usec);

printf(“tz_minuteswest:%d\n”, tz.tz_minuteswest);

printf(“tz_dsttime:%d\n”, tz.tz_dsttime);

return 0;

}

localtime – converts the calendar time timep to broken-down time representation, expressed relative to the user’s specified timezone.

1
2
3
4
struct tm *localtime(const time_t
*timep);
struct tm *localtime_r(const time_t *timep, struct tm
*result);

功能: 把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为日历时间。
说明:此函数获得的tm结构体的时间,是 已经进行过时区转化为本地时间
返回值:返回指向tm 结构体的指针.tm结构体是time.h中定义的用于分别存储时间的各个量(年月日等)的结构体.

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<time.h>
#include<stdio.h>

int main()

{

struct tm *t;

time_t timer;

time(&timer);

t = localtime(&timer);

printf(“%4d year%02d month%02d day %02d:%02d:%02d\n”, t->tm_year + 1900,

t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min,

t->tm_sec);

return 0;

}

// 注意年份上要加上1900。

asctime – converts the broken-down time value tm into a null-terminated string with the same format as ctime()

1
2
3
4
char *asctime(const struct tm
*tm);
char *asctime_r(const struct tm *tm, char
*buf);

功能: 转换日期和时间为相应的ASCII码,返回字符串格式:星期,月,日,小时,分,秒,年与此类似ctime是将相应时间转换为字符串格式。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>
#include <string.h>

#include <time.h>

int main(void)

{

struct tm t;

char str[80];

/* sample loading of tm structure */

t.tm_sec = 1; /* Seconds */

t.tm_min = 30; /* Minutes */

t.tm_hour = 9; /* Hour */

t.tm_mday = 22; /* Day of the Month */

t.tm_mon = 11; /* Month */

t.tm_year = 12; /* Year – does not include century */

t.tm_wday = 4; /* Day of the week */

t.tm_yday = 0; /* Does not show in asctime */

t.tm_isdst = 0; /* Is Daylight SavTime; does not show in asctime */

/* converts structure to null terminated string */

strcpy(str, asctime(&t));

printf(“%s\n”, str);

return 0;

}

ctime – converts the calendar time t into a null-terminated string of the form

1
2
3
4
char
*ctime(const time_t *timep);
char
*ctime_r(const time_t *timep, char *buf);

功能:把time_t描述转换为字符串

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <time.h>

int main(void)

{

time_t timer = time(NULL);

printf(“Today’s data and time is %s\n”, ctime(&timer));

return 0;

}

gmtime – converts the calendar time timep to broken-down time representation, expressed in Coordinated Universal Time (UTC)

1
2
3
4
struct tm *gmtime(const time_t
*timep);
struct tm *gmtime_r(const time_t *timep, struct tm
*result);

功能:把日期和时间转换为格林威治(GMT)时间的函数。将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm返回,获取的时间未经过时区转换。与localtime唯一的区别就是相差了时区,比如对于中国而言,只有小时上相差了8个小时。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<time.h>
#include<stdio.h>

int main()

{

struct tm *t;

time_t timer;

time(&timer);

t = localtime(&timer);

printf(“%4d year%02d month%02d day %02d:%02d:%02d\n”, t->tm_year + 1900,

t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min,

t->tm_sec);

return 0;

}

timegm – inverses of gmtime

1
2
3
#include <time.h>
time_t timegm(struct tm
*tm);

将struct tm结构转成time_t结构,不使用时区信息

mktime – converts a broken-down time structure, expressed as local time, to calendar time representation.

1
time_t mktime(struct tm *tm);

功能:将时间转换为自1970年1月1日以来失去时间的秒数,发生错误是返回-1.

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <time.h>

int main(void)

{

time_t timep;

struct tm *p;

time(&timep);

printf(“time() : %ld \n”, timep);

p = localtime(&timep);

timep = mktime(p);

printf(“time()->localtime()->mktime():%ld\n”, timep);

return 0;

}

timelocal – inverses of localtime

1
2
time_t
timelocal(struct tm *tm);

timelocal 函数是GNU扩展的与posix函数mktime相当

strftime – format date and time

1
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);

函数功能:将时间格式化为一个我们想要的格式,或者说:格式化一个时间字符串。
参数说明:我们可以根据format指向字符串中格式命令把timeptr中保存的时间信息放在strDest指向的字符串中,最多向strDest中存放maxsize个字符。该函数返回向strDest指向的字符串中放置的字符数。
函数strftime()的操作有些类似于sprintf():识别以百分号(%)开始的格式命令集合,格式化输出结果放在一个字符串中。格式化命令说明串strDest中各种日期和时间信息的确切表示方法。格式串中的其他字符原样放进串中。格式命令列在下面,它们是区分大小写的。

格式化 解释
%a 星期几的简写
%A 星期几的全称
%b 月份的简写
%B 月份的全称
%c 标准的日期的时间串
%C 年份的后两位数字
%d 十进制表示的每月的第几天
%D 月/天/年
%e 在两字符域中,十进制表示的每月的第几天
%F 年–月–日
%g 年份的后两位数字,使用基于周的年
%G 年份,使用基于周的年
%h 简写的月份名
%H 24小时制的小时
%I 12小时制的小时
%j 十进制表示的每年的第几天
%m 十进制表示的月份
%M 十时制表示的分钟数
%p 本地的AM或PM的等价显示
%n 新行符
%r 12小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 水平制表符
%T 显示时分秒:hh:mm:ss
%U 第年的第几周,把星期日作为第一天(值从0到53)
%u 每周的第几天,星期一为第一天(值从0到6,星期一为0)
%V 每年的第几周,使用基于周的年
%w 十进制表示的星期几(值从0到6,星期天为0)
%W 每年的第几周,把星期一做为第一天(值从0到53)
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十进制年份(值从0到99)
%Y 带世纪部分的十制年份
%z %Z 时区名称,如果不能得到时区名称则返回空字符。
%% 百分号

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<stdio.h>
#include<time.h>

int main(void)

{

struct tm *newtime;

char tmpbuf[1280];

time_t lt1;

time(&lt1);

newtime = localtime(&lt1);

strftime(tmpbuf, 1280,“Today is\t %A – %a\nMonth is\t %B – %b\nDate is \t%c\nyear- month-day is\t %F\nhour %H\n Time is\t %T\nday %d of %B in the year %Y.\n”, newtime);

printf(“%s\n”, tmpbuf);

return 0;

}

strptime – convert a string representation of time to a time tm structure

1
char *strptime(const char *s, const char *format, struct tm *tm);

功能:按照特定时间格式将字符串转换为时间类型,与上面说的strftime刚好相反。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <time.h>

#include <stdlib.h>

int main(void)

{

char fmt[] = “%Y-%m-%d-%H:%M:%S”;

char buf[] = “2000-01-01-00:00:00”;

struct tm tb;

if (strptime(buf, fmt, &tb) != NULL) {

fprintf(stdout,“ok”);

printf(“\nTime is %s\n”, asctime(&tb));

}

return 0;

}

tzset – function initializes the tzname variable from the TZ environment variable

功能:设置时间环境变量
说明:tzset()函数使用环境变量TZ的当前设置把值赋给三个全局变量:daylight,timezone和tzname。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <time.h>

#include <stdlib.h>

int main(void)

{

time_t td;

putenv(“TZ=PST8PDT”);

tzset();

time(&td);

printf(“Current time = %s\n”, asctime(localtime(&td)));

return 0;

}

关于time_t类型

通过查看宏定义,我们知道time_t实际上是长整型,到未来的某一天,从一个时间点(一般是1970年1月1日0时0分0秒)到那时的秒数(即日历时间)超出了长整形所能表示的数的范围怎么办?对time_t数据类型的值来说,它所表示的时间不能晚于2038年1月18日19时14分07秒。为了能够表示更久远的时间,一些编译器厂商引入了64位甚至更长的整形数来保存日历时间。比如微软在Visual C++中采用了__time64_t数据类型来保存日历时间,并通过_time64()函数来获得日历时间(而不是通过使用32位字的time()函数),这样就可以通过该数据类型保存3001年1月1日0时0分0秒(不包括该时间点)之前的时间。
clock – Determine processor time

1
2
clock_t
clock(void);

功能: 返回处理器调用某个进程或函数所花费的时间。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <time.h>

#include <unistd.h>

int main(void)

{

int i = 0;

clock_t start, end;

start = clock();

while (i < 10000000)

i++;

end = clock();

printf(“The time was: %f\n”,

(double) (end – start) / CLOCKS_PER_SEC);

return 0;

}

注意,在测试进程消耗时间的时候,不要使用sleep,因为sleep不使用CPU时间。
difftime – calculate time difference

1
2
double
difftime(time_t time1, time_t time0);

功 能:返回两个time_t型变量之间的时间间隔,即计算两个时刻之间的时间差
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <time.h>
#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int main(void)

{

time_t first, second;

system(“clear”);

first = time(NULL);

sleep(2);

second = time(NULL);

printf(“The difference is: %f seconds\n”, difftime(second, first));

return 0;

}

no segmentation fault does not means program code is fine

分配页策略,no segmentation fault does not means program code is fine

​ The OS will not allocate partial pages to a program。So some illegal access may not cause some error info. The reason are:

​ 操作系统不会将不完整的页分配给程序,例如,如果要运行的程序总共大约有10000字节,如果完全加载,会占用3个内存页(一个页占4096个字节),它不会仅占用2.5个页,因为页是虚拟内存系统能够操作的最小内存单元,这是调试时要着重了解的情况,这也导致了程序的一些错误内存访问不会触发段错误,换言之,在调试会话期间,没有引起段错误并不能直接说明代码是没有问题的。

Linux的语言设置

Ubuntu

配置文件在/etc/default/locale中。

CentOS

配置文件在/etc/sysconfig/i18n。

修改方法

将 LANG=”zh_CN.UTF-8″ LANGUAGE=”zh_CN:zh”
修改为:LANG=”en_US.UTF-8″ LANGUAGE=”en_US:en”
或者相反。
如果在ssh中中文乱码,可以讲UTF-8改为GB18030。

Linux ln 命令

ln 命令是一个非常重要的命令,可以为某一个文件或目录在其他不同的位置建立一个同步的链接。部分功能与Windows的快捷方式类似。但更加强大。

官方解释为:

ln - make links between files

当我们需要在不同的目录,或者不同的工程,甚至是不同的人员需要用到同一个文件的时候,此时不需要每个位置都通过cp来拷贝一份,因为在源文件更新的时候,这个文件是不会同步更新的 。而此时ln命令就不一样了,通过该命令链接到源文件或目录,不仅可以不用占用重复的更多的磁盘空间,还可以同步更新。NICE

使用格式

1
$ ln [参数][源文件或目录][目标文件或目录]

其中参数的格式为

  • -b ,或 like –backup but does not accept an argument
  • -f,或 --force : 强制执行,这个在链接已经存在的情况下必用
  • -s,或 --symbolic:创建符号链接

在Linux文件系统中,又有两种链接类型:

  1. 硬链接(hard link)
  2. 软链接(symbolic link):又称符号链接,类似于Windows的快捷方式

硬链接会复制一份相同大小的源文件,而软链接是一种特殊的文件,占用很小的磁盘空间。

创建硬链接

默认情况下,不加任何参数,创建的是硬链接,如下,创建源文件a.log的硬链接a1.log

1
2
3
4
5
$ ln a.log a1.log

$ ll
-rw-rw-r--. 3 user user 85710 Apr 5 21:29 a.log
-rw-rw-r--. 3 user user 85710 Apr 5 21:29 a1.log

这个时候修改源文件a.log的部分内容,可以看到硬链接也同步更新。

1
2
3
4
5
$ vim a.log

$ ll
-rw-rw-r--. 3 user user 85716 Apr 5 21:34 a.log
-rw-rw-r--. 3 user user 85716 Apr 5 21:34 a1.log

创建软链接

如果需要创建软链接,就需要参数-s,如下,创建源文件a.log的软链接a1.log

1
2
3
4
5
$ ln -s a.log a1.log

$ ll
-rw-rw-r--. 3 user user 85710 Apr 5 21:29 a.log
lrwxrwxrwx. 1 user user 5 Apr 5 21:30 a1.log -> a.log

这个时候修改源文件a.log的部分内容,可以看到软链接没有更新,不过其指向的内容依然更新了。

1
2
3
4
5
$ vim a.log

$ ll
-rw-rw-r--. 3 user user 85716 Apr 5 21:34 a.log
lrwxrwxrwx. 1 user user 5 Apr 5 21:30 a1.log -> a.log

此时可以看到,对于软链接a1.log而言,其仅为一个符号链接,用file看一下:

1
2
$ file a1.log
a1.log: symbolic link to `a.log'

删除源文件后的情况

此时通过ln创建a.log的硬链接ah.log和软链接as.log,然后看一下如果删除源文件会发生什么情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建软硬链接
$ ln a.log ah.log
$ ln -s a.log as.log
$ ll
-rw-rw-r--. 2 user user 85716 Apr 5 21:34 a.log
lrwxrwxrwx. 1 user user 5 Apr 5 21:30 as.log -> a.log
-rw-rw-r--. 2 user user 85716 Apr 5 21:34 ah.log

# 删除源文件
$ rm a.log

# 此时如果有颜色显示,as.log应该会是红色的警告色
$ ll
lrwxrwxrwx. 1 user user 5 Apr 5 21:30 as.log -> a.log
-rw-rw-r--. 2 user user 85716 Apr 5 21:34 ah.log

# 此时看一下as.log的状态
$ file as.log
as.log: broken symbolic link to `a.log'

可以看到如果删除了源文件,硬链接不受影响,但是软链接已经提示链接损坏了。

强制更新软链接

在软链接存在的情况下,如果再创建一个同名的,会报错,此时就需要强制创建了,加上-f参数即可。

1
2
3
4
5
6
7
8
9
10
11
$ ln -s b.log as.log
ln: failed to create symbolic link 'as.log': File exists

# 强制创建
$ ln -sf b.log as.log

$ ll
-rw-rw-r--. 1 user user 85716 Apr 5 22:16 a.log
-rw-rw-r--. 2 user user 85716 Apr 5 21:34 ah.log
lrwxrwxrwx. 1 user user 5 Apr 5 22:21 as.log -> b.log
-rw-rw-r--. 1 user user 85716 Apr 5 22:17 b.log

Linux ln 命令

ln 命令是一个非常重要的命令,可以为某一个文件或目录在其他不同的位置建立一个同步的链接。部分功能与Windows的快捷方式类似。但更加强大。

官方解释为:

ln - make links between files

当我们需要在不同的目录,或者不同的工程,甚至是不同的人员需要用到同一个文件的时候,此时不需要每个位置都通过cp来拷贝一份,因为在源文件更新的时候,这个文件是不会同步更新的 。而此时ln命令就不一样了,通过该命令链接到源文件或目录,不仅可以不用占用重复的更多的磁盘空间,还可以同步更新。NICE

使用格式

1
$ ln [参数][源文件或目录][目标文件或目录]

其中参数的格式为

  • -b ,或 like –backup but does not accept an argument
  • -f,或 --force : 强制执行,这个在链接已经存在的情况下必用
  • -s,或 --symbolic:创建符号链接

在Linux文件系统中,又有两种链接类型:

  1. 硬链接(hard link)
  2. 软链接(symbolic link):又称符号链接,类似于Windows的快捷方式

硬链接会复制一份相同大小的源文件,而软链接是一种特殊的文件,占用很小的磁盘空间。

创建硬链接

默认情况下,不加任何参数,创建的是硬链接,如下,创建源文件a.log的硬链接a1.log

1
2
3
4
5
$ ln a.log a1.log

$ ll
-rw-rw-r--. 3 user user 85710 Apr 5 21:29 a.log
-rw-rw-r--. 3 user user 85710 Apr 5 21:29 a1.log

这个时候修改源文件a.log的部分内容,可以看到硬链接也同步更新。

1
2
3
4
5
$ vim a.log

$ ll
-rw-rw-r--. 3 user user 85716 Apr 5 21:34 a.log
-rw-rw-r--. 3 user user 85716 Apr 5 21:34 a1.log

创建软链接

如果需要创建软链接,就需要参数-s,如下,创建源文件a.log的软链接a1.log

1
2
3
4
5
$ ln -s a.log a1.log

$ ll
-rw-rw-r--. 3 user user 85710 Apr 5 21:29 a.log
lrwxrwxrwx. 1 user user 5 Apr 5 21:30 a1.log -> a.log

这个时候修改源文件a.log的部分内容,可以看到软链接没有更新,不过其指向的内容依然更新了。

1
2
3
4
5
$ vim a.log

$ ll
-rw-rw-r--. 3 user user 85716 Apr 5 21:34 a.log
lrwxrwxrwx. 1 user user 5 Apr 5 21:30 a1.log -> a.log

此时可以看到,对于软链接a1.log而言,其仅为一个符号链接,用file看一下:

1
2
$ file a1.log
a1.log: symbolic link to `a.log'

删除源文件后的情况

此时通过ln创建a.log的硬链接ah.log和软链接as.log,然后看一下如果删除源文件会发生什么情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建软硬链接
$ ln a.log ah.log
$ ln -s a.log as.log
$ ll
-rw-rw-r--. 2 user user 85716 Apr 5 21:34 a.log
lrwxrwxrwx. 1 user user 5 Apr 5 21:30 as.log -> a.log
-rw-rw-r--. 2 user user 85716 Apr 5 21:34 ah.log

# 删除源文件
$ rm a.log

# 此时如果有颜色显示,as.log应该会是红色的警告色
$ ll
lrwxrwxrwx. 1 user user 5 Apr 5 21:30 as.log -> a.log
-rw-rw-r--. 2 user user 85716 Apr 5 21:34 ah.log

# 此时看一下as.log的状态
$ file as.log
as.log: broken symbolic link to `a.log'

可以看到如果删除了源文件,硬链接不受影响,但是软链接已经提示链接损坏了。

强制更新软链接

在软链接存在的情况下,如果再创建一个同名的,会报错,此时就需要强制创建了,加上-f参数即可。

1
2
3
4
5
6
7
8
9
10
11
$ ln -s b.log as.log
ln: failed to create symbolic link 'as.log': File exists

# 强制创建
$ ln -sf b.log as.log

$ ll
-rw-rw-r--. 1 user user 85716 Apr 5 22:16 a.log
-rw-rw-r--. 2 user user 85716 Apr 5 21:34 ah.log
lrwxrwxrwx. 1 user user 5 Apr 5 22:21 as.log -> b.log
-rw-rw-r--. 1 user user 85716 Apr 5 22:17 b.log