C에 파일이 있는지 확인하는 가장 좋은 방법은 무엇입니까?


436

단순히 파일을 여는 것보다 더 좋은 방법이 있습니까?

int exists(const char *fname)
{
    FILE *file;
    if ((file = fopen(fname, "r")))
    {
        fclose(file);
        return 1;
    }
    return 0;
}

stat 메소드가 매우 합리적인 대안이지만 액세스가 작업을 수행하도록 액세스 메소드에 대한 답변을 제공 할 것이라고 생각합니다.
Dave Marshall

1
당신은 정말로 존재를 확인하고 싶습니까? 또는 확인하고 파일이없는 경우 파일에 쓰십시오. 그렇다면 경쟁 조건으로 고통받지 않는 버전에 대해서는 아래 답변을 참조하십시오.
Dan Lenski

6
나는 보지 않는다-그 fopen / fclose 방법에 무슨 문제가 있는가?
Johannes Schaub-litb

16
@ JohannesSchaub-litb : fopen()/ fclose()메소드의 문제점 중 하나 는 파일이 존재하더라도 읽을 파일을 열지 못할 수 있다는 것입니다. 예를 들어, /dev/kmem존재하지만 대부분의 프로세스는 읽기에도 열 수 없습니다. /etc/shadow또 다른 파일입니다. 물론, 모두 stat()access()파일이 들어있는 디렉토리에 액세스 할 수있는에 의존; 그렇게 할 수 없으면 모든 베팅이 해제됩니다 (파일이있는 디렉토리에 대한 실행 권한이 없음).
Jonathan Leffler

1
if (file = fopen(fname, "r"))경고합니다. if 문 내부에 괄호를 사용하십시오if ((file = fopen(fname, "r")))
Joakim

답변:


595

access()에서 찾은 기능을 찾으십시오 unistd.h. 기능을

if( access( fname, F_OK ) != -1 ) {
    // file exists
} else {
    // file doesn't exist
}

당신은 또한 사용할 수 있습니다 R_OK, W_OK그리고 X_OK대신에 F_OK(읽기 모두 확인 즉, 읽기 권한, 쓰기 권한을 확인하려면, 오히려 존재보다 (각각) 실행 권한을, 당신은 할 수 또는 함께의 사용하여 쓰기 권한 R_OK|W_OK)

업데이트 : Windows에서는 W_OK액세스 기능이 DACL을 고려하지 않으므로 쓰기 권한을 안정적으로 테스트 하는 데 사용할 수 없습니다 . access( fname, W_OK )파일에 읽기 전용 속성이 설정되어 있지 않아서 0 (성공)을 리턴 할 수 있지만 여전히 파일에 쓸 수있는 권한이 없을 수 있습니다.


67
POSIX는 ISO 표준입니다. 그것은 access ()를 정의합니다. C는 또 다른 ISO 표준입니다. 그렇지 않습니다.
Jonathan Leffler

16
access ()와 관련된 함정이 있습니다. access () 사용과 그 이후의 다른 작업 간에는 TOCTOU (확인 시간, 사용 시간) 창이 나타납니다. [... 계속 ...]
Jonathan Leffler

23
[... continuing ...] 좀 더 간결하게 POSIX 시스템에서 access ()는 효과적인 UID와 효과적인 GID가 아닌 실제 UID와 실제 GID인지 확인합니다. 이것은 setuid 또는 setgid 프로그램에만 중요하지만 '잘못된'답변을 줄 수 있으므로 매우 중요합니다.
Jonathan Leffler

3
access()내 코드에서 이유 가 깨진 것을 찾을 때이 질문을 보았습니다 . DevC ++에서 CodeBlocks로 이동하여 작동이 중지되었습니다. 그래서, 그것은 완전하지 않습니다. @Leffler에게 +1 더.
Ben

11
대부분의 경우, 그렇습니다 ( access()파일의 존재를 확인하는 데 사용 하는 것이 좋습니다 ). 그러나 SUID 또는 SGID 프로그램에서는 잘못되었을 수 있습니다. 테스트 된 파일이 실제 UID 또는 실제 GID가 액세스 할 수없는 디렉토리에 있으면 해당 파일이 access()존재하지 않을 때보고하지 않을 수 있습니다. 난해하고 불가능한가? 예.
Jonathan Leffler

116

다음 stat과 같이 사용하십시오 .

#include <sys/stat.h>   // stat
#include <stdbool.h>    // bool type

bool file_exists (char *filename) {
  struct stat   buffer;   
  return (stat (filename, &buffer) == 0);
}

다음과 같이 호출하십시오.

#include <stdio.h>      // printf

int main(int ac, char **av) {
    if (ac != 2)
        return 1;

    if (file_exists(av[1]))
        printf("%s exists\n", av[1]);
    else
        printf("%s does not exist\n", av[1]);

    return 0;
}

4
@LudvigANorin은 : 이러한 시스템에, 기회는 점이다 access()또한 문제가있다, 그리고 수 있도록하는 데 사용하는 옵션이 있습니다 access()stat()(2 GB보다 큰) 큰 파일로 작업.
Jonathan Leffler

14
2GB 이후의 실패에 관한 문서를 가리킬 수 있습니까? 또한 그러한 경우 대안은 무엇입니까?
chamakits

@JonathanLeffler합니까는 stat같은 TOCTOU 취약점으로 고생하지 access? (더 나아질 지 확실하지 않습니다.)
Telemachus

9
둘 다 stat()access()(그래서 않는 TOCTOU 취약점으로 고생 lstat()하지만 fstat()안전하다). 파일의 유무에 따라 수행 할 작업에 따라 다릅니다. 올바른 옵션을 사용하는 open()것이 일반적으로 문제를 처리하는 가장 좋은 방법이지만 올바른 옵션을 공식화하는 것은 까다로울 수 있습니다. EAFP (권한보다 용서를 구하는 것이 더 쉽다) 및 LBYL (Lap Before You Leap)에 대한 토론도 참조하십시오 ( 예 : Java의 LBYL vs EAFP 참조) .
Jonathan Leffler

87

일반적으로 파일이 있는지 확인하려는 경우 해당 파일이 없는 경우 해당 파일 을 만들려고 하기 때문 입니다. Graeme Perrow의 답변 은 해당 파일을 생성 하지 않으려 는 경우에 좋지만, 그렇지 않으면 경쟁 조건에 취약합니다. . (웃지 마 ... 생성 된 파일이 심볼릭 링크라면 보안에 나쁜 영향을 미칠 수 있습니다 !)

당신이 존재를 확인하려는 경우 , 존재하지 않는 경우 파일을 작성 원자 그래서 경쟁 조건이 없는지,이를 사용합니다 :

#include <fcntl.h>
#include <errno.h>

fd = open(pathname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0) {
  /* failure */
  if (errno == EEXIST) {
    /* the file already existed */
    ...
  }
} else {
  /* now you can use the file */
}

8
O_CREAT를 사용하려면 open ()의 세 번째 인수로 모드 (권한)를 제공해야합니다. 또한 O_TRUNC 또는 O_EXCL 또는 O_APPEND를 사용해야하는지 고려하십시오.
Jonathan Leffler

6
Jonathan Leffler가 맞습니다.이 예에서는 O_EXCL이 서면으로 작동해야합니다.
랜디 프록터

6
또한 세 번째 인수로 모드를 지정해야합니다. open (lock, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR)
andrew cooke

4
이것은 파일 시스템이 POSIX를 준수하는 것만 큼 안전합니다. 특히, 이전 버전의 NFS는 O_EXCL이 피해야하는 경쟁 조건을 갖습니다! open(2)(Linux의 경우, OS 설명서 페이지가 다를 수 있음)에 설명되어있는 해결 방법이 있지만 다소 추악하고 악의적 인 공격자에게 내성이 없을 수 있습니다.
Kevin

와 함께 FILE*사용하려면 posix 방법 fdopen(fd,"flags")을 사용하여 FILE*
Gem Taylor

32

예. 사용하십시오 stat(). 에 대한 매뉴얼 페이지를 참조하십시오 stat(2).

stat()파일이 없으면 실패합니다. 그렇지 않으면 성공할 가능성이 큽니다. 존재하지만 디렉토리가 존재하는 디렉토리에 대한 읽기 액세스 권한이 없으면 실패하지만,이 경우 모든 메소드가 실패합니다 (접근 권한에 따라 보이지 않는 디렉토리의 내용을 어떻게 검사 할 수 있습니까? 간단하게는 할 수 없습니다).

다른 사람이 언급했듯이을 사용할 수도 있습니다 access(). 그러나 나는 stat()파일이 존재하는 것처럼 즉시 많은 유용한 정보를 얻습니다 (마지막으로 업데이트되었을 때, 파일을 소유 한 소유자 및 / 또는 그룹, 액세스 권한 등).


5
파일이 존재하는지 알아야하는 경우 액세스가 선호됩니다. 추가 정보가 모두 필요하지 않은 경우 Stat ()에 큰 영향을 줄 수 있습니다.
Martin Beckett

4
실제로 ls-command를 사용하여 디렉토리를 나열하면 거기에있는 모든 파일에 대해 stat를 호출하고 실행중인 ls에 큰 오버 헤드가 있다는 것은 나에게 새로운 것입니다. 실제로 수천 개의 파일이있는 디렉토리에서 ls를 실행할 수 있으며 순식간에 리턴됩니다.
Mecki

2
@Mecki : stat는 하드 링크를 지원하는 시스템의 액세스와 비교하여 추가 오버 헤드가 0이 아닙니다. stat는 inode도 검색해야하는 반면, 액세스는 디렉토리 항목 만 볼 수 있기 때문입니다. 검색 시간이 나쁜 저장 장치 (예 : 테이프)에서는 디렉토리 항목과 inode가 서로 인접하지 않기 때문에 차이가 클 수 있습니다.
Kevin

3
@Kevin access()파일에 F_OK 만 전달하지 않는 한 파일의 파일 액세스 권한을 확인하고 해당 파일에 대한 inode에 저장되며 디렉토리 항목에는 없습니다 (적어도 inode와 같은 구조를 가진 모든 파일 시스템에 대해) . 따라서 access()inode에 액세스하는 것과 동일한 방식으로 inode stat()에 액세스해야합니다. 따라서 권한을 확인하지 않으면 말 그대로 적용됩니다! 그리고 실제로 일부 시스템 access()에서는 stat()(예를 들어 GNU Hurd의 glibc가 그런 식으로) 구현하기 때문에 처음에는 보장 할 수 없습니다.
Mecki

@Mecki : 누가 말했다 아무 권한 확인에 대한 있습니까? 나는 구체적으로 F_OK에 대해 이야기하고있었습니다. 그렇습니다. 일부 시스템은 제대로 구현되지 않았습니다. 액세스는 모든 경우에있어 최소한 스탯만큼 빠르며 시간이 더 빠를 수 있습니다.
Kevin

9
FILE *file;
    if((file = fopen("sample.txt","r"))!=NULL)
        {
            // file exists
            fclose(file);
        }
    else
        {
            //File not found, no memory leak since 'file' == NULL
            //fclose(file) would cause an error
        }

1
메모리 누수가 발생하지 않습니까? 파일이 있으면 닫지 마십시오.
LegionMammal978

1
이것은 좋고 간단한 방법입니다. Windows MSVC에있는 경우이를 대신 사용하십시오. (fopen_s(file, "sample.txt", "r"))since fopen()는 더 이상 사용되지 않는 것으로 간주됩니다 (또는 더 이상 사용되지 않는 오류를 비활성화하지만 권장하지는 않습니다).
Nikos

15
fopen()표준 C이고, 아무데도 가지 않습니다. Microsoft에서는 "더 이상 사용하지 않습니다". fopen_s()이식 가능하지 않은 플랫폼 별 코드를 원하지 않는 한 사용 하지 마십시오 .
Andrew Henle

아무 것도 fclose () 호출? 변수 'file'을 먼저 할당해야했습니다!
Jenix

1
여기서 'file'변수에는 가비지 값이 있습니다. 왜 처음에 그것을 닫는 것을 귀찮게합니까? 당신은 'fclose (SOME_RANDOM_ADDRESS);'..
Jenix

6

Visual C ++ 도움말에서 나는 함께 갈 경향이 있습니다.

/* ACCESS.C: This example uses _access to check the
 * file named "ACCESS.C" to see if it exists and if
 * writing is allowed.
 */

#include  <io.h>
#include  <stdio.h>
#include  <stdlib.h>

void main( void )
{
   /* Check for existence */
   if( (_access( "ACCESS.C", 0 )) != -1 )
   {
      printf( "File ACCESS.C exists\n" );
      /* Check for write permission */
      if( (_access( "ACCESS.C", 2 )) != -1 )
         printf( "File ACCESS.C has write permission\n" );
   }
}

또한 모드 값을 주목할 가치가 있습니다 ._access(const char *path,int mode)

  • 00 : 존재 만

  • 02 : 쓰기 권한

  • 04 : 읽기 권한

  • 06 : 읽기 및 쓰기 권한

당신은 fopen파일이 존재하지만 요청으로 열 수 없습니다 상황에서 실패 할 수 있습니다.

편집 : 그냥 Mecki의 게시물을 읽으십시오. stat()더 깔끔한 방법으로 보입니다. 흠.


파일이 존재하는지 알아야하는 경우 액세스가 선호됩니다. Stat ()는 큰 값을 가질 수 있습니다.
Martin Beckett

4

realpath () 함수를 사용할 수 있습니다.

resolved_file = realpath(file_path, NULL);
if (!resolved_keyfile) {
   /*File dosn't exists*/
   perror(keyfile);
   return -1;
}

3

나는 access () 함수 unistd.h가 좋은 선택 이라고 생각합니다 ( statLinux사용할 수 있습니다 ).

다음과 같이 사용할 수 있습니다.

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>

void fileCheck(const char *fileName);

int main (void) {
    char *fileName = "/etc/sudoers";

    fileCheck(fileName);
    return 0;
}

void fileCheck(const char *fileName){

    if(!access(fileName, F_OK )){
        printf("The File %s\t was Found\n",fileName);
    }else{
        printf("The File %s\t not Found\n",fileName);
    }

    if(!access(fileName, R_OK )){
        printf("The File %s\t can be read\n",fileName);
    }else{
        printf("The File %s\t cannot be read\n",fileName);
    }

    if(!access( fileName, W_OK )){
        printf("The File %s\t it can be Edited\n",fileName);
    }else{
        printf("The File %s\t it cannot be Edited\n",fileName);
    }

    if(!access( fileName, X_OK )){
        printf("The File %s\t is an Executable\n",fileName);
    }else{
        printf("The File %s\t is not an Executable\n",fileName);
    }
}

그리고 당신은 다음과 같은 결과를 얻습니다 :

The File /etc/sudoers    was Found
The File /etc/sudoers    cannot be read
The File /etc/sudoers    it cannot be Edited
The File /etc/sudoers    is not an Executable
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.