1.文件的創建和讀寫
當我們需要打開一個文件進行讀寫操作的時候,我們可以使用系統調用函數open.使用完成以後我們調用另外一個close函數進行關閉操作.
int open(const char *pathname,int flags);
int open(const char*pathname,int flags,mode_t mode);
int close(int fd);
open函數有兩個形式.其中pathname是我們要打開的文件名(包含路徑名稱,缺省是認爲在當前路徑下面).flags可以去下面的一個值或者是幾個值的組合.
O_RDONLY :以只讀的方式打開文件.
O_WRONLY :以只寫的方式打開文件.
O_RDWR :以讀寫的方式打開文件.
O_APPEND :以追加的方式打開文件.
O_CREAT :創建一個文件.
O_EXEC :如果使用了O_CREAT而且文件已經存在,就會發生一個錯誤.
O_NOBLOCK :以非阻塞的方式打開一個文件.
O_TRUNC :如果文件已經存在,則刪除文件的內容.
前面三個標誌只能使用任意的一個.如果使用了O_CREATE標誌,那麼我們要使用open的第二種形式。還要指定mode標誌,用來表示文件的訪問權限。mode可以是以下情況的組合.
-------------------------------------------------
S_IRUSR 用戶可以讀 S_IWUSR 用戶可以寫
S_IXUSR 用戶可以執行 S_IRWXU 用戶可以讀寫執行
-------------------------------------------------
S_IRGRP 組可以讀 S_IWGRP 組可以寫
S_IXGRP 組可以執行 S_IRWXG 組可以讀寫執行
-------------------------------------------------
S_IROTH 其他人可以讀 S_IWOTH 其他人可以寫
S_IXOTH 其他人可以執行 S_IRWXO 其他人可以讀寫執行
-------------------------------------------------
S_ISUID 設置用戶執行ID S_ISGID 設置組的執行ID
-------------------------------------------------
我們也可以用數字來代表各個位的標誌.Linux總共用5個數字來表示文件的各種權限.
第一位表示設置用戶ID.
第二位表示設置組ID,
第三位表示用戶自己的權限位,
第四位表示組的權限,
最後一位表示其他人的權限.
每個數字可以取1(執行權限),2(寫權限),4(讀權限),0(什麼也沒有)或者是這幾個值的和. 比如我們要創建一個用戶讀寫執行,組沒有權限,其他人讀執行的文件。設置用戶ID位那麼我們可以使用的模式是--1(設置用戶ID)0(組沒有設置)7(1+2+4)0(沒有權限,使用缺省)5(1+4)即10705:
open("temp",O_CREAT,10705);
如果我們打開文件成功,open會返回一個文件描述符.我們以後對文件的所有操作就可以對這個文件描述符進行操作了.
當我們操作完成以後,我們要關閉文件了,只要調用close就可以了,其中fd是我們要關閉的文件描述符.
文件打開了以後,我們就要對文件進行讀寫了.我們可以調用函數read和write進行文件的讀寫.
ssize_t read(int fd, void *buffer,size_tcount);
ssize_t write(int fd, const void*buffer,size_t count);
fd是我們要進行讀寫操作的文件描述符.
buffer是我們要寫入文件內容或讀出文件內容的內存地址.
count是我們要讀寫的字節數.
對於普通的文件read從指定的文件(fd)中讀取count字節到buffer緩衝區中(記住我們必須提供一個足夠大的緩衝區),同時返回count.
如果read讀到了文件的結尾或者被一個信號所中斷,返回值會小於count.
如果是由信號中斷引起返回,而且沒有返回數據,read會返回-1,且設置errno爲EINTR.
當程序讀到了文件結尾的時候,read會返回0.
write從buffer中寫count字節到文件fd中,成功時返回實際所寫的字節數.
下面我們學習一個實例,這個實例用來拷貝文件,該程序已檢測通過
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#define BUFFER_SIZE 1024
int main(int argc,char **argv)
{
int from_fd,to_fd;
int bytes_read,bytes_write;
char buffer[BUFFER_SIZE];
char *ptr;
if(argc!=3)
{
fprintf(stderr,"Usage:%s fromfiletofile\n\a",argv[0]);
exit(1);
}
if((from_fd=open(argv[1],O_RDONLY))==-1)
{
fprintf(stderr,"Open %sError:%s\n",argv[1],strerror(errno));
exit(1);
}
if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1)
{
fprintf(stderr,"Open %sError:%s\n",argv[2],strerror(errno));
exit(1);
}
while(bytes_read=read(from_fd,buffer,BUFFER_SIZE))
{
if((bytes_read==-1)&&(errno!=EINTR)) break;
else if(bytes_read>0)
{
ptr=buffer;
while(bytes_write=write(to_fd,ptr,bytes_read))
{
if((bytes_write==-1)&&(errno!=EINTR))break;
else if(bytes_write==bytes_read) break;
else if(bytes_write>0)
{
ptr+=bytes_write;
bytes_read-=bytes_write;
}
}
if(bytes_write==-1)break;
}
}
close(from_fd);
close(to_fd);
exit(0);
}
2.文件的各個屬性
文件具有各種各樣的屬性,除了我們上面所知道的文件權限以外,文件還有創建時間,大小等等屬性.有時侯我們要判斷文件是否可以進行某種操作(讀,寫等等).這個時候我們可以使用access函數.
int access(const char *pathname,int mode);
pathname是文件名稱,
mode是我們要判斷的屬性.可以取以下值或者是他們的組合.
R_OK文件可以讀,W_OK文件可以寫,X_OK文件可以執行,F_OK文件存在.
當我們測試成功時,函數返回0,否則如果有一個條件不符時,返回-1.
如果我們要獲得文件的其他屬性,我們可以使用函數stat或者fstat.
int stat(const char *file_name,struct stat *buf);
int fstat(int filedes,struct stat *buf);
struct stat
{
dev_t st_dev; /* 設備 */
ino_t st_ino; /* 節點 */
mode_t st_mode; /* 模式 */
nlink_t st_nlink; /* 硬連接 */
uid_t st_uid; /* 用戶ID */
gid_t st_gid; /* 組ID */
dev_t st_rdev; /* 設備類型 */
off_t st_off; /* 文件字節數 */
unsigned long st_blksize; /* 塊大小 */
unsigned long st_blocks; /* 塊數 */
time_t st_atime; /* 最後一次訪問時間 */
time_t st_mtime; /* 最後一次修改時間 */
time_t st_ctime; /* 最後一次改變時間(指屬性) */
};
stat用來判斷沒有打開的文件,而fstat用來判斷打開的文件.我們使用最多的屬性是st_mode.通過着屬性我們可以判斷給定的文件是一個普通文件還是一個目錄,連接等等.可以使用下面幾個宏來判斷.
S_ISLNK(st_mode):是否是一個連接.
S_ISREG是否是一個常規文件.
S_ISDIR是否是一個目錄.
S_ISCHR是否是一個字符設備.
S_ISBLK是否是一個塊設備.
S_ISFIFO是否是一個FIFO文件.
S_ISSOCK是否是一個SOCKET文件.
3.目錄文件的操作
在我們編寫程序的時候,有時候會要得到我們當前的工作路徑。C庫函數提供了getcwd來解決這個問題。
char *getcwd(char *buffer,size_t size);
我們提供一個size大小的buffer,getcwd會把我們當前的路徑考到buffer中.如果buffer太小,函數會返回-1和一個錯誤號.
Linux提供了大量的目錄操作函數,我們學習幾個比較簡單和常用的函數:
int mkdir(const char *path,mode_tmode);
DIR *opendir(const char *path);
struct dirent *readdir(DIR *dir);
void rewinddir(DIR *dir);
off_t telldir(DIR *dir);
void seekdir(DIR *dir,off_t off);
int closedir(DIR *dir);
struct dirent
{
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[NAME_MAX+1]; /* 文件名稱 */
}
mkdir:很容易就是我們創建一個目錄.
opendir:打開一個目錄爲以後讀做準備.
readdir:讀一個打開的目錄.
rewinddir:是用來重讀目錄的,和我們學的rewind函數一樣.
closedir:是關閉一個目錄.
telldir和seekdir類似與ftell和fseek函數.
下面我們開發一個小程序,這個程序有一個參數.如果這個參數是一個文件名,我們輸出這個文件的大小和最後修改的時間,如果是一個目錄我們輸出這個目錄下所有文件的大小和修改時間.
static int get_file_size_time(const char *filename)
{
struct stat statbuf;
if(stat(filename,&statbuf)==-1)
{
printf("Get stat on %s Error:%s\n",
filename,strerror(errno));
return(-1);
}
if(S_ISDIR(statbuf.st_mode))return(1);
if(S_ISREG(statbuf.st_mode))
printf("%s size:%ld bytes\tmodified at %s",
filename,statbuf.st_size,ctime(&statbuf.st_mtime));
return(0);
}
int main(int argc,char **argv)
{
DIR *dirp;
struct dirent *direntp;
int stats;
if(argc!=2)
{
printf("Usage:%sfilename\n\a",argv[0]);
exit(1);
}
if(((stats=get_file_size_time(argv[1]))==0)||(stats==-1))
exit(1);
if((dirp=opendir(argv[1]))==NULL)
{
printf("Open Directory %s Error:%s\n",
argv[1],strerror(errno));
exit(1);
}
while((direntp=readdir(dirp))!=NULL)
if(get_file_size_time(direntp-br> closedir(dirp);
exit(1);
}
4.管道文件
Linux提供了許多的過濾和重定向程序,比如more cat等等.還提供了< > | <<等等重定向操作符.在這些過濾和重定向程序當中,都用到了管道這種特殊的文件.系統調用pipe可以創建一個管道.
int pipe(int fildes[2]);
pipe調用可以創建一個管道(通信緩衝區).當調用成功時,我們可以訪問文件描述符fildes[0],fildes[1].其中fildes[0]是用來讀的文件描述符,而fildes[1]是用來寫的文件描述符.
在實際使用中我們是通過創建一個子進程,然後一個進程寫,一個進程讀來使用的.
#define BUFFER 255
int main(int argc,char **argv)
{
char buffer[BUFFER+1];
int fd[2];
if(argc!=2)
{
fprintf(stderr,"Usage:%sstring\n\a",argv[0]);
exit(1);
}
if(pipe(fd)!=0)
{
fprintf(stderr,"PipeError:%s\n\a",strerror(errno));
exit(1);
}
if(fork()==0)
{
close(fd[0]);
printf("Child[%d] Write topipe\n\a",getpid());
snprintf(buffer,BUFFER,"%s",argv[1]);
write(fd[1],buffer,strlen(buffer));
printf("Child[%d] Quit\n\a",getpid());
exit(0);
}
else
{
close(fd[1]);
printf("Parent[%d] Read frompipe\n\a",getpid());
memset(buffer,'\0',BUFFER+1);
read(fd[0],buffer,BUFFER);
printf("Parent[%d]Read:%s\n",getpid(),buffer);
exit(1);
}
}
爲了實現重定向操作,我們需要調用另外一個函數dup2.
int dup2(int oldfd,int newfd);
dup2將用oldfd文件描述符來代替newfd文件描述符,同時關閉newfd文件描述符.也就是說,所有向newfd操作都轉到oldfd上面.下面我們學習一個例子,這個例子將標準輸出重定向到一個文件.
#define BUFFER_SIZE 1024
int main(int argc,char **argv)
{
int fd;
char buffer[BUFFER_SIZE];
if(argc!=2)
{
fprintf(stderr,"Usage:%soutfilename\n\a",argv[0]);
exit(1);
}
if((fd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR))==-1)
{
fprintf(stderr,"Open %s Error:%s\n\a",argv[1],strerror(errno));
exit(1);
}
if(dup2(fd,STDOUT_FILENO)==-1)
{
fprintf(stderr,"Redirect Standard OutError:%s\n\a",strerror(errno));
exit(1);
}
fprintf(stderr,"Now,please input string");
fprintf(stderr,"(To quit use CTRL+D)\n");
while(1)
{
fgets(buffer,BUFFER_SIZE,stdin);
if(feof(stdin))break;
write(STDOUT_FILENO,buffer,strlen(buffer));
}
exit(0);
}
5.其他文件操作函數
5.01 access 判斷是否具有存取文件的權限
頭文 件
#include<unistd.h>
定 義
int access(const char * pathname,int mode);
函數說明
access()會檢查是否可以讀/寫某一已存在的文件。參數mode有幾種情況組合,R_OK,W_OK,X_OK 和F_OK。R_OK,W_OK與X_OK用來檢查文件是否具有讀取、寫入和執行的權限。F_OK則是用來判斷該文件是否存在。由於access()只作權限的核查,並不理會文件形態或文件內容,因此,如果一目錄表示爲"可寫入",表示可以在該目錄中建立新文件等操作,而非意味此目錄可以被當做文件處理。例如,你會發現DOS的文件都具有"可執行"權限,但用execve()執行時則會失敗。
返回 值
若所有欲查覈的權限都通過了檢查則返回0值,表示成功,只要有一權限被禁止則返回-1。
錯誤代碼
EACCESS 參數pathname 所指定的文件不符合所要求測試的權限。
EROFS 欲測試寫入權限的文件存在於只讀文件系統內。
EFAULT 參數pathname指針超出可存取內存空間。
EINVAL 參數mode 不正確。
ENAMETOOLONG參數pathname太長。
ENOTDIR 參數pathname爲一目錄。
ENOMEM 核心內存不足
ELOOP 參數pathname有過多符號連接問題。
EIO I/O 存取錯誤。
附加說明
使用access()作用戶認證方面的判斷要特別小心,例如在access()後再做open()的空文件可能會造成系統安全上的問題。
範 例
/* 判斷是否允許讀取/etc/passwd */
#include<unistd.h>
int main()
{
if (access("/etc/passwd",R_OK) = =0)
printf("/etc/passwd can be read\n");
}
5.02 alphasort 依字母順序排序目錄結構
頭文 件
#include<dirent.h>
定 義
int alphasort(const struct dirent **a,const structdirent **b);
函數說明
alphasort()爲scandir()最後調用qsort()函數時傳給qsort()作爲判斷的函數,詳細說明請參考scandir()及qsort()。
函數說明
參考qsort()。
範 例
/* 讀取/目錄下所有的目錄結構,並依字母順序排列*/
main()
{
struct dirent **namelist;
int i,total;
total = scandir("/",&namelist,0,alphasort);
if(total <0)
perror("scandir");
else
{
for(i=0;i<total;i++)
printf("%s\n",namelist[i]->d_name);
printf("total = %d\n",total);
}
}
5.03 chdir 改變當前的工作目錄
頭文 件
#include<unistd.h>
定義函數
int chdir(const char * path);
函數說明
chdir()用來將當前的工作目錄改變成以參數path所指的目錄。
函數說明
執行成功則返回0,失敗返回-1,errno爲錯誤代碼。
範 例
#include<unistd.h>
main()
{
chdir("/tmp");
printf("current working directory:%s\n",getcwd(NULL,NULL));
}
5.04 chmod 改變文件的權限
頭文 件
#include<sys/types.h>
#include<sys/stat.h>
定 義
int chmod(const char * path,mode_t mode);
函數說明
chmod()會依參數mode 權限來更改參數path 指定文件的權限。
參數 mode 有下列數種組合
S_ISUID 04000 文件的(set user-id on execution)位
S_ISGID 02000 文件的(set group-id on execution)位
S_ISVTX 01000 文件的sticky位
S_IRUSR(S_IREAD) 00400 文件所有者具可讀取權限
S_IWUSR(S_IWRITE)00200 文件所有者具可寫入權限
S_IXUSR(S_IEXEC) 00100 文件所有者具可執行權限
S_IRGRP 00040 用戶組具可讀取權限
S_IWGRP 00020 用戶組具可寫入權限
S_IXGRP 00010 用戶組具可執行權限
S_IROTH 00004 其他用戶具可讀取權限
S_IWOTH 00002 其他用戶具可寫入權限
S_IXOTH 00001 其他用戶具可執行權限
只有該文件的所有者或有效用戶識別碼爲0,纔可以修改該文件權限。基於系統安全,如果欲將數據寫入一執行文件,而該執行文件具有S_ISUID 或S_ISGID 權限,則這兩個位會被清除。如果一目錄具有S_ISUID 位權限,表示在此目錄下只有該文件的所有者或root可以刪除該文件。
函數說明
權限改變成功返回0,失敗返回-1,錯誤原因存於errno。
錯誤代碼
EPERM 進程的有效用戶識別碼與欲修改權限的文件擁有者不同,而且也不具root權限。
EACCESS 參數path所指定的文件無法存取。
EROFS 欲寫入權限的文件存在於只讀文件系統內。
EFAULT 參數path指針超出可存取內存空間。
EINVAL 參數mode不正確
ENAMETOOLONG 參數path太長
ENOENT 指定的文件不存在
ENOTDIR 參數path路徑並非一目錄
ENOMEM 核心內存不足
ELOOP 參數path有過多符號連接問題。
EIO I/O存取錯誤
範 例
/* 將/etc/passwd 文件權限設成S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH */
#include<sys/types.h>
#include<sys/stat.h>
main()
{
chmod("/etc/passwd",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
}
5.05 chown 改變文件的所有者
頭文 件
#include<sys/types.h>
#include<unistd.h>
定 義
int chown(const char * path, uid_t owner,gid_t group);
函數說明
chown()會將參數path指定文件的所有者變更爲參數owner代表的用戶,而將該文件的組變更爲參數group組。如果參數owner或group爲-1,對應的所有者或組不會有所改變。root與文件所有者皆可改變文件組,但所有者必須是參數group組的成員。當root用chown()改變文件所有者或組時,該文件若具有S_ISUID或S_ISGID權限,則會清除此權限位,此外如果具有S_ISGID權限但不具S_IXGRP位,則該文件會被強制鎖定,文件模式會保留。
函數說明
成功則返回0,失敗返回-1,錯誤原因存於errno。
錯誤代碼
參考chmod()。
範 例
/* 將/etc/passwd 的所有者和組都設爲root */
#include<sys/types.h>
#include<unistd.h>
main()
{
chown("/etc/passwd",0,0);
}
5.06 chroot 改變根目錄
頭文 件
#include<unistd.h>
定 義
int chroot(const char * path);
函數說明
chroot()用來改變根目錄爲參數path 所指定的目錄。只有超級用戶才允許改變根目錄,子進程將繼承新的根目錄。
函數說明
調用成功則返回0,失敗則返-1,錯誤代碼存於errno。
錯誤代碼
EPERM 權限不足,無法改變根目錄。
EFAULT 參數path指針超出可存取內存空間。
ENAMETOOLONG 參數path太長。
ENOTDIR 路徑中的目錄存在但卻非真正的目錄。
EACCESS 存取目錄時被拒絕
ENOMEM 核心內存不足。
ELOOP 參數path有過多符號連接問題。
EIO I/O存取錯誤。
範 例
/* 將根目錄改爲/tmp ,並將工作目錄切換至/tmp */
#include<unistd.h>
main()
{
chroot("/tmp");
chdir("/");
}
5.07 closedir 關閉目錄
頭文 件
#include<sys/types.h>
#include<dirent.h>
定 義
int closedir(DIR *dir);
函數說明
closedir()關閉參數dir所指的目錄流。
函數說明
關閉成功則返回0,失敗返回-1,錯誤原因存於errno 中。
錯誤代碼
EBADF 參數dir爲無效的目錄流
範 例
參考readir()。
5.08 fchdir 改變當前的工作目錄
頭文 件
#include<unistd.h>
定 義
int fchdir(int fd);
函數說明
fchdir()用來將當前的工作目錄改變成以參數fd 所指的文件描述詞。
函數說明
行成功則返回0,失敗返回-1,errno爲錯誤代碼。
附加說明
範 例
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
main()
{
int fd;
fd = open("/tmp",O_RDONLY);
fchdir(fd);
printf("current working directory : %s\n",getcwd(NULL,NULL));
close(fd);
}
5.09 fchmod 改變文件的權限
頭文 件
#include<sys/types.h>
#include<sys/stat.h>
定 義
int fchmod(int fildes,mode_t mode);
函數說明
fchmod()會依參數mode權限來更改參數fildes所指文件的權限。參數fildes爲已打開文件的文件描述詞。參數mode請參考chmod()。
函數說明
權限改變成功則返回0,失敗返回-1,錯誤原因存於errno。
錯誤原因
EBADF 參數fildes爲無效的文件描述詞。
EPERM 進程的有效用戶識別碼與欲修改權限的文件所有者不同,而且也不具root權限。
EROFS 欲寫入權限的文件存在於只讀文件系統內。
EIO I/O存取錯誤。
範 例
#include<sys/stat.h>
#include<fcntl.h>
main()
{
int fd;
fd = open ("/etc/passwd",O_RDONLY);
fchmod(fd,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
close(fd);
}
5.10 fchown 改變文件的所有者
頭文 件
#include<sys/types.h>
#include<unistd.h>
定 義
int fchown(int fd,uid_t owner,gid_t group);
函數說明
fchown()會將參數fd指定文件的所有者變更爲參數owner代表的用戶,而將該文件的組變更爲參數group組。如果參數owner或group爲-1,對映的所有者或組有所改變。參數fd 爲已打開的文件描述詞。當root用fchown()改變文件所有者或組時,該文件若具S_ISUID或S_ISGID權限,則會清除此權限位。
函數說明
成功則返回0,失敗則返回-1,錯誤原因存於errno。
錯誤代碼
EBADF 參數fd文件描述詞爲無效的或該文件已關閉。
EPERM 進程的有效用戶識別碼與欲修改權限的文件所有者不同,而且也不具root權限,或是參數owner、group不正確。
EROFS 欲寫入的文件存在於只讀文件系統內。
ENOENT 指定的文件不存在
EIO I/O存取錯誤
範 例
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
main()
{
int fd;
fd = open ("/etc/passwd",O_RDONLY);
chown(fd,0,0);
close(fd);
}
5.11 fstat 由文件描述詞取得文件狀態
頭文 件
#include<sys/stat.h>
#include<unistd.h>
定 義
int fstat(int fildes,struct stat *buf);
函數說明
fstat()用來將參數fildes所指的文件狀態,複製到參數buf所指的結構中(struct stat)。Fstat()與stat()作用完全相同,不同處在於傳入的參數爲已打開的文件描述詞。詳細內容請參考stat()。
函數說明
執行成功則返回0,失敗返回-1,錯誤代碼存於errno。
範 例
#include<sys/stat.h>
#include<unistd.h>
#include<fcntk.h>
main()
{
struct stat buf;
int fd;
fd = open ("/etc/passwd",O_RDONLY);
fstat(fd,&buf);
printf("/etc/passwd file size +%d\n",buf.st_size);
}
5.12 ftruncate 改變文件大小
頭文 件
#include<unistd.h>
定 義
int ftruncate(int fd,off_t length);
函數說明
ftruncate()會將參數fd指定的文件大小改爲參數length指定的大小。參數fd爲已打開的文件描述詞,而且必須是以寫入模式打開的文件。如果原來的文件大?炔問齦ength大,則超過的部分會被刪去。
函數說明
執行成功則返回0,失敗返回-1,錯誤原因存於errno。
錯誤代碼
EBADF 參數fd文件描述詞爲無效的或該文件已關閉。
EINVAL 參數fd 爲一socket 並非文件,或是該文件並非以寫入模式打開。
5.13 getcwd 取得當前的工作目錄
頭文 件
#include<unistd.h>
定 義
char * getcwd(char * buf,size_t size);
函數說明
getcwd()會將當前的工作目錄絕對路徑複製到參數buf所指的內存空間,參數size爲buf的空間大小。在調用此函數時,buf所指的內存空間要足夠大,若工作目錄絕對路徑的字符串長度超過參數size大小,則回值NULL,errno的值則爲ERANGE。倘若參數buf爲NULL,getcwd()會依參數size的大小自動配置內存(使用malloc()),如果參數size也爲0,則getcwd()會依工作目錄絕對路徑的字符串程度來決定所配置的內存大小,進程可以在使用完此字符串後利用free()來釋放此空間。
函數說明
執行成功則將結果複製到參數buf所指的內存空間,或是返回自動配置的字符串指針。失敗返回NULL,錯誤代碼存於errno。
範 例
#include<unistd.h>
main()
{
char buf[80];
getcwd(buf,sizeof(buf));
printf("current working directory :%s\n",buf);
}
5.14 link 建立文件連接
頭文 件
#include<unistd.h>
定 義
int link (const char * oldpath,const char * newpath);
函數說明
link()以參數newpath指定的名稱來建立一個新的連接(硬連接)到參數oldpath所指定的已存在文件。如果參數newpath指定的名稱爲一已存在的文件則不會建立連接。
函數說明
成功則返回0,失敗返回-1,錯誤原因存於errno。
附加說明
link()所建立的硬連接無法跨越不同文件系統,如果需要請改用symlink()。
錯誤代碼
EXDEV 參數oldpath與newpath不是建立在同一文件系統。
EPERM 參數oldpath與newpath所指的文件系統不支持硬連接
EROFS 文件存在於只讀文件系統內
EFAULT 參數oldpath或newpath 指針超出可存取內存空間。
ENAMETOLLONG 參數oldpath或newpath太長
ENOMEM 核心內存不足
EEXIST 參數newpath所指的文件名已存在。
EMLINK 參數oldpath所指的文件已達最大連接數目。
ELOOP 參數pathname有過多符號連接問題
ENOSPC 文件系統的剩餘空間不足。
EIO I/O存取錯誤。
範 例
/* 建立/etc/passwd 的硬連接爲pass */
#include<unistd.h>
main()
{
link("/etc/passwd","pass");
}
5.15 lstat 由文件描述詞取得文件狀態
頭文 件
#include<sys/stat.h>
#include<unistd.h>
定 義
int lstat (const char * file_name.struct stat * buf);
函數說明
lstat()與stat()作用完全相同,都是取得參數file_name所指的文件狀態,其差別在於,當文件爲符號連接時,lstat()會返回該link本身的狀態。詳細內容請參考stat()。
函數說明
執行成功則返回0,失敗返回-1,錯誤代碼存於errno。
範 例
參考stat()。
5.16 opendir 打開目錄
頭文 件
#include<sys/types.h>
#include<dirent.h>
定 義
DIR * opendir(const char * name);
函數說明
opendir()用來打開參數name指定的目錄,並返回DIR*形態的目錄流,和open()類似,接下來對目錄的讀取和搜索都要使用此返回值。
函數說明
成功則返回DIR* 型態的目錄流,打開失敗則返回NULL。
錯誤代碼
EACCESS 權限不足
EMFILE 已達到進程可同時打開的文件數上限。
ENFILE 已達到系統可同時打開的文件數上限。
ENOTDIR 參數name非真正的目錄
ENOENT 參數name 指定的目錄不存在,或是參數name 爲一空字符串。
ENOMEM 核心內存不足。
5.17 readdir 讀取目錄
頭文 件
#include<sys/types.h>
#include<dirent.h>
定 義
struct dirent * readdir(DIR * dir);
函數說明
readdir()返回參數dir目錄流的下個目錄進入點。
結構定義
struct dirent
{
ino_t d_ino;
ff_t d_off;
signed short int d_reclen;
unsigned char d_type;
har d_name[256;
};
d_ino 此目錄進入點的inode
d_off 目錄文件開頭至此目錄進入點的位移
d_reclen d_name的長度,不包含NULL字符
d_type d_name 所指的文件類型
d_name 文件名
函數說明
成功則返回下個目錄進入點。有錯誤發生或讀取到目錄文件尾則返回NULL。
錯誤代碼
EBADF 參數dir爲無效的目錄流。
範 例
#include<sys/types.h>
#include<dirent.h>
#include<unistd.h>
main()
{
DIR * dir;
struct dirent * ptr;
int i;
dir =opendir("/etc/rc.d");
while((ptr = readdir(dir))!=NULL)
{
printf("d_name: %s\n",ptr->d_name);
}
closedir(dir);
}
5.18 readlink 取得符號連接所指的文件
頭文 件
#include<unistd.h>
定 義
int readlink(const char * path ,char * buf,size_tbufsiz);
函數說明
readlink()會將參數path的符號連接內容存到參數buf所指的內存空間,返回的內容不是以NULL作字符串結尾,但會將字符串的字符數返回。若參數bufsiz小於符號連接的內容長度,過長的內容會被截斷。
函數說明
執行成功則傳符號連接所指的文件路徑字符串,失敗則返回-1,錯誤代碼存於errno。
錯誤代碼
EACCESS 取文件時被拒絕,權限不夠
EINVAL 參數bufsiz 爲負數
EIO I/O存取錯誤。
ELOOP 欲打開的文件有過多符號連接問題。
ENAMETOOLONG 參數path的路徑名稱太長
ENOENT 參數path所指定的文件不存在
ENOMEM 核心內存不足
ENOTDIR 參數path路徑中的目錄存在但卻非真正的目錄。
5.19 remove 刪除文件
頭文 件
#include<stdio.h>
定 義
int remove(const char * pathname);
函數說明
remove()會刪除參數pathname指定的文件。如果參數pathname爲一文件,則調用unlink()處理,若參數pathname爲一目錄,則調用rmdir()來處理。請參考unlink()與rmdir()。
函數說明
成功則返回0,失敗則返回-1,錯誤原因存於errno。
錯誤代碼
EROFS 欲寫入的文件存在於只讀文件系統內
EFAULT 參數pathname指針超出可存取內存空間
ENAMETOOLONG 參數pathname太長
ENOMEM 核心內存不足
ELOOP 參數pathname有過多符號連接問題
EIO I/O存取錯誤。
5.20 rename 更改文件名稱或位置
頭文 件
#include<stdio.h>
定 義
int rename(const char * oldpath,const char * newpath);
函數說明
rename()會將參數oldpath 所指定的文件名稱改爲參數newpath所指的文件名稱。若newpath所指定的文件已存在,則會被刪除。
函數說明
執行成功則返回0,失敗返回-1,錯誤原因存於errno
範 例
/* 設計一個DOS下的rename指令rename 舊文件名新文件名*/
#include <stdio.h>
void main(int argc,char **argv)
{
if(argc<3)
{
printf("Usage: %s old_name new_name\n",argv[0]);
return;
}
printf("%s=>%s",argc[1],argv[2]);
if(rename(argv[1],argv[2]<0)
printf("error!\n");
else
printf("ok!\n");
}
5.21 rewinddir 重設讀取目錄的位置爲開頭位置
頭文 件
#include<sys/types.h>
#include<dirent.h>
定 義
void rewinddir(DIR *dir);
函數說明
rewinddir()用來設置參數dir 目錄流目前的讀取位置爲原來開頭的讀取位置。
錯誤代碼
EBADF dir爲無效的目錄流
範 例
#include<sys/types.h>
#include<dirent.h>
#include<unistd.h>
main()
{
DIR * dir;
struct dirent *ptr;
dir = opendir("/etc/rc.d");
while((ptr = readdir(dir))!=NULL)
{
printf("d_name :%s\n",ptr->d_name);
}
rewinddir(dir);
printf("readdir again!\n");
while((ptr = readdir(dir))!=NULL)
{
printf("d_name: %s\n",ptr->d_name);
}
closedir(dir);
}
5.22 seekdir 設置下回讀取目錄的位置
頭文 件
#include<dirent.h>
定 義
void seekdir(DIR * dir,off_t offset);
函數說明
seekdir()用來設置參數dir目錄流目前的讀取位置,在調用readdir()時便從此新位置開始讀取。參數offset 代表距離目錄文件開頭的偏移量。
錯誤代碼
EBADF 參數dir爲無效的目錄流
範 例
#include<sys/types.h>
#include<dirent.h>
#include<unistd.h>
main()
{
DIR * dir;
struct dirent * ptr;
int offset,offset_5,i=0;
dir=opendir("/etc/rc.d");
while((ptr = readdir(dir))!=NULL)
{
offset = telldir(dir);
if(++i = =5) offset_5 =offset;
printf("d_name :%s offset :%d\n",ptr->d_name,offset);
}
seekdir(dir offset_5);
printf("Readdir again!\n");
while((ptr = readdir(dir))!=NULL)
{
offset = telldir(dir);
printf("d_name :%s offset :%d\n",ptr->d_name.offset);
}
closedir(dir);
}
5.23 stat 取得文件狀態
頭文 件
#include<sys/stat.h>
#include<unistd.h>
定 義
int stat(const char * file_name,struct stat *buf);
函數說明
stat()用來將參數file_name所指的文件狀態,複製到參數buf所指的結構中。
結構定義
struct stat
{
dev_t st_dev; /*device*/
ino_t st_ino; /*inode*/
mode_t st_mode; /*protection*/
nlink_t st_nlink; /*number of hard links */
uid_t st_uid; /*user ID of owner*/
gid_t st_gid; /*group ID of owner*/
dev_t st_rdev; /*device type */
off_t st_size; /*total size, in bytes*/
unsigned long st_blksize; /*blocksize for filesystemI/O */
unsigned long st_blocks; /*number of blocksallocated*/
time_t st_atime; /* time of lastaccess*/
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last change */
};
st_dev 文件的設備編號
st_ino 文件的i-node
st_mode 文件的類型和存取的權限
st_nlink 連到該文件的硬連接數目,剛建立的文件值爲1。
st_uid 文件所有者的用戶識別碼
st_gid 文件所有者的組識別碼
st_rdev 若此文件爲裝置設備文件,則爲其設備編號
st_size 文件大小,以字節計算
st_blksize 文件系統的I/O 緩衝區大小。
st_blocks 佔用文件區塊的個數,每一區塊大小爲512 個字節。
st_atime 文件最近一次被存取或被執行的時間,一般只有在用mknod、utime、read、write與tructate時改變。
st_mtime 文件最後一次被修改的時間,一般只有在用mknod、utime和write時纔會改變
st_ctime i-nod最近一次被更改的時間,此參數會在文件所有者、組、權限被更改時更新先前所描述的
st_mode 則定義了下列數種情況
S_IFMT 0170000 文件類型的位遮罩
S_IFSOCK 0140000 scoket
S_IFLNK 0120000 符號連接
S_IFREG 0100000 一般文件
S_IFBLK 0060000 區塊裝置
S_IFDIR 0040000 目錄
S_IFCHR 0020000 字符裝置
S_IFIFO 0010000 先進先出
S_ISUID 04000 文件的(set user-id on execution)位
S_ISGID 02000 文件的(set group-id on execution)位
S_ISVTX 01000 文件的sticky位
S_IRUSR(S_IREAD) 00400 文件所有者具可讀取權限
S_IWUSR(S_IWRITE) 00200 文件所有者具可寫入權限
S_IXUSR(S_IEXEC) 00100 文件所有者具可執行權限
S_IRGRP 00040 用戶組具可讀取權限
S_IWGRP 00020 用戶組具可寫入權限
S_IXGRP 00010 用戶組具可執行權限
S_IROTH 00004 其他用戶具可讀取權限
S_IWOTH 00002 其他用戶具可寫入權限
S_IXOTH 00001 其他用戶具可執行權限
上述的文件類型在POSIX 中定義了檢查這些類型的宏定義
S_ISLNK(st_mode) 判斷是否爲符號連接
S_ISREG(st_mode) 是否爲一般文件
S_ISDIR(st_mode) 是否爲目錄
S_ISCHR(st_mode) 是否爲字符裝置文件
S_ISBLK(s3e) 是否爲先進先出
S_ISSOCK(st_mode) 是否爲socket
若一目錄具有sticky 位(S_ISVTX),則表示在此目錄下的文件只能被該文件所有者、此目錄所有者或root來刪除或改名。
函數說明
執行成功則返回0,失敗返回-1,錯誤代碼存於errno
錯誤代碼
ENOENT 參數file_name指定的文件不存在
ENOTDIR 路徑中的目錄存在但卻非真正的目錄
ELOOP 欲打開的文件有過多符號連接問題,上限爲16符號連接
EFAULT 參數buf爲無效指針,指向無法存在的內存空間
EACCESS 存取文件時被拒絕
ENOMEM 核心內存不足
ENAMETOOLONG 參數file_name的路徑名稱太長
範 例
#include<sys/stat.h>
#include<unistd.h>
main()
{
struct stat buf;
stat ("/etc/passwd",&buf);
printf("/etc/passwd file size = %d\n",buf.st_size);
}
5.24 symlink 建立文件符號連接
頭文 件
#include<unistd.h>
定 義
int symlink( const char * oldpath,const char *newpath);
函數說明
symlink()以參數newpath指定的名稱來建立一個新的連接(符號連接)到參數oldpath所指定的已存在文件。參數oldpath指定的文件不一定要存在,如果參數newpath指定的名稱爲一已存在的文件則不會建立連接。
函數說明
成功則返回0,失敗返回-1,錯誤原因存於errno。
錯誤代碼
EPERM 參數oldpath與newpath所指的文件系統不支持符號連接
EROFS 欲測試寫入權限的文件存在於只讀文件系統內
EFAULT 參數oldpath或newpath指針超出可存取內存空間。
ENAMETOOLONG 參數oldpath或newpath太長
ENOMEM 核心內存不足
EEXIST 參數newpath所指的文件名已存在。
EMLINK 參數oldpath所指的文件已達到最大連接數目
ELOOP 參數pathname有過多符號連接問題
ENOSPC 文件系統的剩餘空間不足
EIO I/O存取錯誤
範 例
#include<unistd.h>
main()
{
symlink("/etc/passwd","pass");
}
5.25 telldir 取得目錄流的讀取位置
頭文 件
#include<dirent.h>
定 義
off_t telldir(DIR *dir);
函數說明
telldir()返回參數dir目錄流目前的讀取位置。此返回值代表距離目錄文件開頭的偏移量返回值返回下個讀取位置,有錯誤發生時返回-1。
錯誤代碼
EBADF 參數dir爲無效的目錄流。
範 例
#include<sys/types.h>
#include<dirent.h>
#include<unistd.h>
main()
{
DIR *dir;
struct dirent *ptr;
int offset;
dir = opendir("/etc/rc.d");
while((ptr = readdir(dir))!=NULL)
{
offset = telldir (dir);
printf("d_name : %s offset :%d\n",ptr->d_name,offset);
}
closedir(dir);
}
5.26 truncate 改變文件大小
頭文 件
#include<unistd.h>
定 義
int truncate(const char * path,off_t length);
函數說明
truncate()會將參數path 指定的文件大小改爲參數length 指定的大小。如果原來的文件大?炔問齦ength大,則超過的部分會被刪去。
函數說明
執行成功則返回0,失敗返回-1,錯誤原因存於errno。
錯誤代碼
EACCESS 參數path所指定的文件無法存取。
EROFS 欲寫入的文件存在於只讀文件系統內
EFAULT 參數path指針超出可存取內存空間
EINVAL 參數path包含不合法字符
ENAMETOOLONG 參數path太長
ENOTDIR 參數path路徑並非一目錄
EISDIR 參數path 指向一目錄
ETXTBUSY參數path所指的文件爲共享程序,而且正被執行中
ELOOP 參數path’有過多符號連接問題
EIO I/O存取錯誤。
5.27 umask 設置建立新文件時的權限遮罩
頭文 件
#include<sys/types.h>
#include<sys/stat.h>
定 義
mode_t umask(mode_t mask);
函數說明
umask()會將系統umask值設成參數mask&0777後的值,然後將先前的umask值返回。在使用open()建立新文件時,該參數mode並非真正建立文件的權限,而是(mode&~umask)的權限值。例如,在建立文件時指定文件權限爲0666,通常umask值默認爲022,則該文件的真正權限則爲0666&~022=0644,也就是rw-r--r--返回值此調用不會有錯誤值返回。
函數說明
原先系統的umask值。
5.28 unlink 刪除文件
頭文 件
#include<unistd.h>
定 義
int unlink(const char * pathname);
函數說明
unlink()會刪除參數pathname指定的文件。如果該文件名爲最後連接點,但有其他進程打開了此文件,則在所有關於此文件的文件描述詞皆關閉後纔會刪除。如果參數pathname爲一符號連接,則此連接會被刪除。
函數說明
成功則返回0,失敗返回-1,錯誤原因存於errno
錯誤代碼
EROFS 文件存在於只讀文件系統內
EFAULT 參數pathname指針超出可存取內存空間
ENAMETOOLONG 參數pathname太長
ENOMEM 核心內存不足
ELOOP 參數pathname 有過多符號連接問題
EIO I/O存取錯誤
5.29 utime 修改文件的存取時間和更改時間
頭文 件
#include<sys/types.h>
#include<utime.h>
定 義
int utime(const char * filename,struct utimbuf * buf);
函數說明
utime()用來修改參數filename文件所屬的inode存取時間。
結構定義
struct utimbuf
{
time_t actime;
time_t modtime;
};
函數說明
如果參數buf爲空指針(NULL),則該文件的存取時間和更改時間全部會設爲目前時間。執行成功則返回0,失敗返回-1,錯誤代碼存於errno。
錯誤代碼
EACCESS 存取文件時被拒絕,權限不足
ENOENT 指定的文件不存在。
5.30 utimes 修改文件的存取時間和更改時間
頭文 件
#include<sys/types.h>
#include<utime.h>
定 義
int utimes(char * filename.struct timeval *tvp);
函數說明
utimes()用來修改參數filename文件所屬的inode存取時間和修改時間。
結構定義
struct timeval
{
long tv_sec;
long tv_usec; /* 微妙*/
};
函數說明
參數tvp 指向兩個timeval 結構空間,和utime()使用的utimebuf結構比較,tvp[0].tc_sec 則爲utimbuf.actime,tvp]1].tv_sec爲utimbuf.modtime。執行成功則返回0。失敗返回-1,錯誤代碼存於errno。
錯誤代碼
EACCESS 存取文件時被拒絕,權限不足
ENOENT 指定的文件不存在