I write this code to print all files in /home/keep with absolution path:
#include <dirent.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void catDIR(const char *re_path);
int main(int argc, char *argv[])
{
char *top_path = "/home/keep";
catDIR(top_path);
return 0;
}
void catDIR(const char *re_path)
{
DIR *dp;
struct stat file_info;
struct dirent *entry;
if ((dp = opendir(re_path)) == NULL)
{
perror("opendir");
return;
}
while (entry = readdir(dp))
{
if (entry->d_name[0] == '.')
continue;
//get Absolute path
char next_path[PATH_MAX];
strcpy(next_path, re_path);
strcat(next_path, "/");
strcat(next_path, entry->d_name);
lstat(next_path, &file_info);
if (S_ISDIR(file_info.st_mode))
{
catDIR(next_path);
}
else
{
printf("%s/%s\n", re_path, entry->d_name);
}
}
free(dp);
free(entry);
}
When I run it.
Not only print some file's path, but also print some error message:
opendir: Too many open files
I read man 3 opendir, then realize, I had opened too many files.
I do want to know, how to close it? and how to correct this program
You should probably use closedir when you finish iterating through a directory's contents.
Also, you might want to read the directory's listing into an array, close the directory, and then recurse on the array. This might help with traversing very deep directory structures.
Run
cat /proc/sys/fs/file-nr
what does it give ?
output format is : (number of allocated file handlers) - (number of allocated but unused file handlers) - (maximum number of file handlers)
If you get more number of allocated but unused file handlers, it means that you've to close directories as mentioned by #Matthew Iselin.
You can also change system limit.
More info about changing system limit Can be found here.
Related
I want to calculate the size of the directory (path) recursively. In my current code I have a function that identifies if it's a directory or file, if it's a directory it calls the function with the subdirectory (file) and if it's a file it adds to the totalSize variable. However, my current code doesn't return anything meaning that there is an error somewhere.
here is my code -
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
void getsize(const char *path,int* totalSize);
int main()
{
int total = 0;
char path [] = "C:\\Users\\abbas\\Desktop\\Leetcode airplane";
getsize(path,&total);
printf("%d",total);
return total;
}
void getsize(const char *path,int* totalSize)
{
struct dirent *pDirent;
struct stat buf;
struct stat info;
DIR *pDir;
int exists;
char str[100];
pDir = opendir (path);
while ((pDirent = readdir(pDir)) != NULL)
{
stat(pDirent->d_name,&info);
if(S_ISDIR(info.st_mode))
{
strcpy(str,path);
strcat(str,"/");
strcat(str,pDirent->d_name);
getsize(str,totalSize);
}
else
{
strcpy(str,path);
strcat(str,"/");
strcat(str,pDirent->d_name);
exists = stat(str,&buf);
if (exists < 0)
{
continue;
}
else
{
(*totalSize) += buf.st_size;
}
}
}
closedir(pDir);
}
Include string.h.
The arbitrary fixed sizestr[100] is problematic. If you are on Linux include linux/limits.h and use str[PATH_MAX] or even better pathconf(path, _PC_NAME_MAX). In either case you should either ensure the buffer is big enough (using snprintf() for instance), or dynamically allocate the buffer.
You need to exclude . and .. otherwise you end up with an infinite loop (path/../path/..).
stat(pDirent->d_name,&info) fails as you need to stat() path/pDirect->d_name not just pDirect->d_name.
(not fixed) Maybe snprintf(path2, sizeof path2, "%s%s%s", path, PATH_SEP, pDirenv->d_name) instead of strcpy() and strcat()?
Check return values of functions otherwise you are wasting time.
No point of doing two stat() calls on the same path so just use (*totalSize) += buf.st_size;.
(not fixed) On Windows, consider using _stat64() with the address of a struct __stat64 (#AndrewHenle).
I assume you only want the size of files.
(not fixed) It would be more natural if getsize() returned the size instead of using int *totalSize out parameter.
(not fixed) Consider using nftw() (or the older ftw()) to walk the tree.
Note that program now accept path via command line for testing purposes.
#include <dirent.h>
#include <errno.h>
#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
const char PATH_SEP =
#ifdef _WIN32
"\\";
#else
"/";
#endif
void getsize(const char *path,int *totalSize) {
struct dirent *pDirent;
DIR *pDir = opendir (path);
while ((pDirent = readdir(pDir)) != NULL) {
if(
!strcmp(pDirent->d_name, ".") ||
!strcmp(pDirent->d_name, "..")
)
continue;
char path2[PATH_MAX];
strcpy(path2, path);
strcat(path2, PATH_SEP);
strcat(path2, pDirent->d_name);
struct stat info;
if(stat(path2, &info) == -1) {
perror("stat");
return;
}
if(S_ISDIR(info.st_mode))
getsize(path2, totalSize);
else if(S_ISREG(info.st_mode))
(*totalSize) += info.st_size;
}
closedir(pDir);
}
int main(argc, char *argv[]) {
if(argc != 2) {
printf("usage: your_program path\n");
return 1;
}
int total = 0;
getsize(argv[1], &total);
printf("%d\n",total);
}
and example test:
$ mkdir -p 1/2
$ dd if=/dev/zero of=1/file count=123
123+0 records in
123+0 records out
62976 bytes (63 kB, 62 KiB) copied, 0.000336838 s, 187 MB/s
$ dd if=/dev/zero of=1/2/file count=234
234+0 records in
234+0 records out
119808 bytes (120 kB, 117 KiB) copied, 0.0015842 s, 75.6 MB/s
$ echo $((62976 + 119808))
182784
$ ./your_program 1
182784
I think the major error of your code lies in the recursive logic.
To quote pp.183 of The C Programming Language:
Each directory always contains entries for itself, called ".",
and its parent, ".."; these must be skipped, or the program will
loop forever.
Therefore, maybe you can try adding the following if test
at the beginning of the while loop:
while ((pDirent = readdir(pDir)) != NULL)
{
if (strcmp(pDirent->d_name, ".") == 0
|| strcmp(pDirent->d_name, "..") == 0)
continue; /* skip self and parent */
/* ... */
}
Still, there might be other errors, but I think this one is the most significant.
Practice safe coding.
Below risks buffer overflow.
// Risky
strcpy(str,path);
strcat(str,"/");
strcat(str,pDirent->d_name);
Had code done,
int len = snprintf(str, sizeof str, "%s/%s", path, pDirent->d_name);
if (len < 0 || (unsigned) len >= sizeof str) {
fprintf(stderr, "Path too long %s/%s\n", path, pDirent->d_name);
exit (-1);
}
Then the code would have readily errored out do to recursion on "." and ".." and led to OP's self-discovery of a key problem.
This make for faster code production and more resilient code. Saves OP time.
this is my current code:
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
int main(int argc, char *argv[]){
struct stat file_stat;
DIR *dirp;
struct dirent* dent;
long size = 0;
if(argc > 1){
dirp = opendir(argv[1]);
printf("%s",argv[1]);
}
if(argc == 1){
argv[1] = ".";
dirp = opendir(argv[1]);
printf("dir .\n");
do{
dent = readdir(dirp);
if(dent){
char* fileName = dent->d_name;
if(fileName[0] == '.'){
continue;
}
printf("\t%s\n",dent->d_name);
if(!lstat(dent->d_name, &file_stat)){
printf("\t%d:",(int) file_stat.st_size);
size += (int)
file_stat.st_size;
}
}
}while(dent);
printf("\n\nTotal file space used:%ld\n",size);
closedir(dirp);
}
}
As of right now I only have it printing the files of the current directory.
My output is printing all my correct file sizes and file names. But it's printing it out of order. Each line should have the "fileSize: fileName". Currently it prints the first filename and the prints the size on the next line. Making each Filename have the size of the file before it. My output is below
dir .
p7.c.save
564: p7.c.save.3
466: p7
8632: Makefile
44: n
0: p7.c
779: p7.c.save.4
384: p7.c.save.2
360: p7.c.save.1
559:
This is what my output should roughly look like. I am newer to C and have been struggling with fixing the format of my output. If anyone could help me out or point me in the right direction I would greatly appreciate it!!!
$ ./p7
dir .
17371:p8
170:Makefile
1715:p8.c
11416:p8.o
Total file space used:30672
I need to find number of files and folders in a directory. I'm using MinGW compiler before this I tried to use d_type but I couldn't compile my code.
And I don't care about "." and" .. " directory. I don't wanna calculate them.
So I write a program like this. This program can easily find how many files and folders in a directory.
But When I give a new Name a folder instead of New Folder, New Folder(1). This programs calculate that folders as a file.
What can I do? I really stuck. I have to find how many files and how many folders...
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int
main(int argc, char *argv[])
{
int file_count = 0;
int dir_count = 0;
struct dirent * entry;
struct stat filestat;
size_t nfiles = 0, ndirs = 0;
DIR *dp;
if (argc != 2)
{
printf("usage: put directory_name\n");
exit(-1);
}
if ((dp = opendir(argv[1])) == NULL)
{
printf("Error: can't open %s\n", argv[1]);
exit(-2);
}
while ((entry= readdir(dp)) != NULL){
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
continue;
}
stat(entry->d_name,&filestat);
if( S_ISDIR(filestat.st_mode) ){
ndirs++;
}
else
nfiles++;
}
closedir(dp);
printf("%lu Files, %lu Directories\n", nfiles, ndirs);
return(0);
}
entry->d_name will contain the filename only, not the full path, so if call stat() it will fail unless that file happens to exist in the current directory.
Also note that your code is not recursive, so it won't count stuff in the subfolders.
I was wondering if someone could tell me what I'm doing wrong. This code is supposed to walk though all the directories and files and print them out exactly the same way the UNIX utility FIND does. But for some reason I cant get chdir to change the working directory. I'm trying to limit the number of file descriptors im using.
MAIN
#include <stdio.h>
#include "sfind.h"
#include <unistd.h>
#include <dirent.h>
int main(int argv, char *argc[]){
char cwd[1024]; /* current working directory limit*/
char *path = NULL;
DIR *dp = NULL;
if (getcwd(cwd, sizeof(cwd)) != NULL){ /*allow us to grab the current working directory*/
fprintf(stdout, "Current working dir: %s\n", cwd);
}
else{
perror("getcwd() error");
}
dp = opendir(cwd);
path = ".";
directoryList(dp,path);
return 0;
}
Directory Method Definition
#include <stdio.h>
#include <stdlib.h>
#include "sfind.h"
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
void directoryList(DIR *dp, char *path){
char newPath[PATH_MAX] = {0};/*To store new path*/
struct dirent *element; /*get file name*/
struct stat statbuf;/*determine type of file*/
int status = 0; /*My base case should be once the directory I'm in runs outs out of files I should return;*/
if(dp == NULL){
fprintf(stderr,"FILE DID NOT OPEN!");
exit(-1);
}
/*change the current file directory even if its the first one*/
if((status = chdir(path)) == -1){
printf("ERROOR!");
}/*change the current working directory whether that the same on or not*/
while((element = readdir(dp)) != NULL) /*while current file directory pointer is not equal to zero*/{
/* from here we only have two cases once were reading from the directory either is a file or directory!*/
/*using lstat*/
lstat(element->d_name,&statbuf);
if((S_ISDIR(statbuf.st_mode))) /*is of type directory*/{
if((strcmp(".",element->d_name) == 0) || (strcmp("..",element->d_name) == 0))
continue;
/*create new directory name*/
newPath[0] = '\0';
strcat(newPath,path);/* this will give us the "."*/
strcat(newPath,"/");
strcat(newPath,element->d_name);
printf("%s\n", newPath);
directoryList(dp,newPath); /*recursion*/
file*/
}
else /*Its a file!*/{
printf("%s/%s\n",path,element->d_name);
}
}
}
The issue seems to be with the call to readdir(dp)...
Even though you change the current working directory, you don't update the dp pointer to open the new folder.
Here's a poor-man's working example (I wouldn't do it this way, but it works for small trees).
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void directoryList(DIR *dp, char *path) {
char newPath[PATH_MAX] = {0}; /*To store new path*/
struct dirent *element; /*get file name*/
struct stat statbuf; /*determine type of file*/
int status = 0; /*My base case should be once the directory I'm in runs outs
out of files I should return;*/
DIR *dp_tmp;
if (dp == NULL) {
fprintf(stderr, "FILE DID NOT OPEN!");
exit(-1);
}
while ((element = readdir(dp)) !=
NULL) /*while current file directory pointer is not equal to zero*/ {
/* from here we only have two cases once were reading from the directory
* either is a file or directory!*/
/*using lstat*/
lstat(element->d_name, &statbuf);
if ((S_ISDIR(statbuf.st_mode))) /*is of type directory*/ {
if ((strcmp(".", element->d_name) == 0) ||
(strcmp("..", element->d_name) == 0))
continue;
/*create new directory name*/
newPath[0] = '\0';
strcat(newPath, path); /* this will give us the "."*/
strcat(newPath, "/");
strcat(newPath, element->d_name);
printf("%s\n", newPath);
if ((dp_tmp = opendir(newPath)) == NULL) {
perror("hmm?! ");
exit(1);
}
directoryList(dp_tmp, newPath); /*recursion*/
} else /*Its a file!*/ {
printf("* %s/%s\n", path, element->d_name);
}
}
closedir(dp);
}
int main(void) {
char cwd[1024]; /* current working directory limit*/
char *path = NULL;
DIR *dp = NULL;
if (getcwd(cwd, sizeof(cwd)) !=
NULL) { /*allow us to grab the current working directory*/
fprintf(stdout, "Current working dir: %s\n", cwd);
} else {
perror("getcwd() error");
}
dp = opendir(cwd);
path = ".";
directoryList(dp, path);
return 0;
}
EDIT:
To answer the question in the comment...
Open directories (that should be closed using closedir) are different (and quite unrelated) to the current working directory.
The current working directory is mostly used to resolve the path to any file/folder you're referencing.
Open directory pointers (DIR *) are just pointers to data in the memory. That data relates to a specific directory and you can open a number of directories at the same time.
EDIT2:
A few people in the comments recommended nftw (file tree walk) which is a great alternative to doing it yourself.
If this isn't a learning project, I would recommend it's use.
However, note that POSIX.1-2008 marks ftw as obsolete, so make sure to use the nftw flavor.
Is your goal to learn to implement this yourself, or do you just want results? Because you should take a look at fts.h if you want some very powerful stuff to implement something like find.
I have files something like this:
file1_a_etc.txt,
file1_b_etc.txt
file2_a_z.txt
file2_b_z.txt
I want to get the size of files with "a" i.e. file2_a_z.txt & file1_a_etc.txt
I have got a large number of files this way, so cant specify each name individually.
I am a beginner at C.
I know how to read the size of a single file. And I am working on windows.
#include <stdio.h>
#include <sys/stat.h> // For struct stat and stat()
struct stat attr;
void main()
{
if(stat("filename.txt", &attr) == 0)
{
float x;
x=(attr.st_size)/1048576.0; //1MB=1048576 bytes
printf("Filesize: %.2f MB", x);
}
else
{
// couldn't open the file
printf("Couldn't get file attributes...");
}
}
For Windows console there is function _findfirst. For first parameter put *a*.txt.
You need to iterate over the files in a given directory while searching for the substring in each file name.
This answer, under the section (Unix/Linux), specifies how to iterate over each filename while comparing for an exact match, you can modify the strcmp function call to strstr to look for a substring.
You could make an Array of strings to store all filenames. Then you can use the strchr function to test, if an 'a' or other character is the name. The use of this function is explained e.g at http://www.tutorialspoint.com/ansi_c/c_strchr.htm
Reading directories programmatically can be done with readdir.
You could do something like this:
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
static void lookup(const char *dir)
{
DIR *dirp;
struct dirent *dp;
if ((dirp = opendir(dir)) == NULL) {
perror("couldn't open '.'");
return;
}
do {
errno = 0;
if ((dp = readdir(dirp)) != NULL) {
if (strstr(dp->d_name, "_a_") == NULL)
continue;
(void) printf("found %s\n", dp->d_name);
// Add code to handle the file
}
} while (dp != NULL);
if (errno != 0)
perror("error reading directory");
(void) closedir(dirp);
return;
}
readdir is part of POSIX.1-2001, which is supported by unix/linux-type systems (including OS/X) but only some windows compilers. If you are programming in windows you may have to use another solution.