This question already has answers here:
stat() error 'No such file or directory' when file name is returned by readdir()
(2 answers)
Closed 3 years ago.
I have the following files/directories in my current directory. test_folder1 is a directory and there is one more directory in that directory. My C code is supposed to print all the files/directories in the current directory recursively. However, it only prints the current directory and one level down subdirectory, it does not go beyond that. Please help.
Current Directory:
a.out at.c dt dt.c main.c README test.c test_folder1.
Subdirectory of test_folder1:
ahmet.txt mehmet.txt test_folder2.
Subdirectory of test_folder2:
mahmut.txt
This for mac terminal C code.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <limits.h>
void depthFirst(DIR *dir){
struct dirent *sd;
char path[PATH_MAX];
if(dir == NULL){
printf("Error, unable to open\n");
exit(1);
}
while( (sd = readdir(dir)) != NULL){
if(strcmp(sd->d_name, ".") != 0 && strcmp(sd->d_name, "..") != 0){
printf("%s\n", sd->d_name);
realpath(sd->d_name,path);
if(isdirectory(path)){
depthFirst(opendir(sd->d_name));
}
}
}
}
int isdirectory(char *path) {
struct stat statbuf;
if (stat(path, &statbuf) == -1)
return 0;
else
return S_ISDIR(statbuf.st_mode);
}
int main(int argc, char *argv[]){
if(argc<2){
printf("No arguments");
DIR *dir;
dir = opendir(".");
depthFirst(dir);
closedir(dir);
}
This is the output
README
main.c
test.c
test_folder1
ahmet.txt
mehmet.txt
test_folder2
a.out
at.c
dt
dt.c
At the point where you're calling realpath(sd->d_name, path) for test_folder2, your current working directory is still . rather than test_folder1, so realpath() is using for ./test_folder2 rather than ./test_folder1/test_folder2.
As a result, path is the absolute path to a would-be ./test_folder2 and not ./test_folder1/test_folder2, and so your stat() call fails, meaning that test_folder2 is not a directory and therefore depthFirst() isn't called for it.
What you need to do is:
Upon entry to depthFirst(), save the current working directory (getcwd()) in some local variable and change directory (chdir()) to the directory you have as a parameter.
Before exiting depthFirst(), change directory back to the previous working directory.
You may want to have depthFirst() receive a path as a string and do the opendir() call by itself.
Let's suppose you have the following structure (program starts outside of dir_origin with dir_origin as argument):
dir_origin
README.md
dir_p0:
file1.txt
dir_p1:
file_1_1.txt
file_1_2.txt
dir_p2:
single.txt
some_other_files.txt
at this point (first recursive call):
if(isdirectory(path)){
depthFirst(opendir(sd->d_name));
You're trying to operate on dir_p0 but the process still working in the directory dir_origin/.., so you need to enter the parent of the directory you want to process first (which is dir_origin), you can do this by calling chdir on the parent directory before every recursive call to the depthFirst() and restore the working directory after the recursive call by calling chdir again with ..
Another solution to avoid changing working directory is to keep building the full path for subdirectories by joining the current path, file separator ('/') and the sub-directory to be processed before the recursive call.
Related
I have two files in the same directory.
directory/
| a.c
| b.c
a.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
pid_t pid;
int status;
int wret;
if ((pid = fork()) < 0)
printf("error");
else if(pid == 0)
{
printf("%s", argv[1]);
execv(argv[1], &argv[1]);
}
else
{
/* respawn */
if ((wret = wait(&status)) != -1)
execv(argv[1], &argv[1]);
}
return 0;
}
b.c is just a simple program that print "hello".
I want to run ./a b from the command line to make the a program call exexXX to execute the b program.
I don't understand why if I use execv I can write just ./a b in the command line, instead if I use execvp I have to write ./a ./b.
The man exec page is not clear because it reports
"The initial argument for these functions is the name of a file that
is to be executed."
Thanks
If the program name argument contains no slashes, the execvp() function looks for the program to execute in the directories listed on your PATH environment variable. If you don't have . (the current directory) on your PATH and you aren't in one of the directories listed on your path, a plain name like b will not be executed, even if b is in the current directory. If the name contains a slash, it can be relative (./b) or absolute (/home/someone/src/programs/b) and it will be interpreted as a file name to be executed without consulting the PATH environment variable.
By contrast, execv() treats a plain b in the program name argument as ./b — the name of the file in the current directory and executes it if it is present, and fails if it is located somewhere else.
At one time, there was a comment that asked:
Are you saying if you have an executable b in . and you do execv("b", b_args), it will get executed?
On a normal Unix box, yes.
Code b.c:
#include <stdio.h>
int main(void)
{
puts("Hello");
return 0;
}
Code a.c:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char *argv[] = { "b", 0 };
execv(argv[0], argv);
fprintf(stderr, "failed to execute '%s'\n", argv[0]);
return 1;
}
Running these:
$ (PATH=$(clnpath "$PATH" ".:$PWD"); echopath PATH; ./a)
/Users/jleffler/bin
/opt/informix/12.10.FC6/bin
/Users/jleffler/oss/bin
/Users/jleffler/oss/rcs/bin
/usr/local/mysql/bin
/opt/gcc/v7.3.0/bin
/Users/jleffler/perl/v5.24.0/bin
/usr/local/bin
/usr/bin
/bin
/opt/gnu/bin
/usr/sbin
/sbin
Hello
$
The clnpath script modifies the string provided as its first argument ("$PATH") by removing any occurrences of any of the directory names listed in its second path-like argument (".:$PWD") — it's how I edit my PATH on the fly when I need to. The echopath script echoes the directories on PATH (or any other path-like variable, or it will process the result of expanding a pathlike variable, such as "$PATH"), one per line — the output shows that neither . nor /Users/jleffler/soq (which is where I run the program) is on $PATH in the sub-shell. The ./a runs the code from a.c (it would not be executed without that ./ in front), which in turn runs the code from b.c, which produces the Hello. (If there is some system where this does not work, please identify it.)
I could also arrange for b.c to be:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
puts("Hello");
const char *env = "PATH";
char *val = getenv(env);
if (val == 0)
val = "<nothing>";
printf("%s=%s\n", env, val);
return 0;
}
which would print the value of $PATH directly from the executable (to verify that neither . nor the value of the current working directory is listed).
I'm trying to code the ls command in C, but stat() refuse to open any other directory.
~/Desktop/ls$ cat bug.c
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <unistd.h>
int main(int ac, char **av)
{
DIR *d;
struct dirent *dir;
struct stat file;
d = opendir(av[1]);
if (d)
{
while ((dir = readdir(d)) != NULL)
{
printf("%s ->", dir->d_name);
if (lstat(dir->d_name, &file) < 0)
printf(" can't read file %s!", dir->d_name);
printf("\n");
}
}
closedir(d);
return (0);
}
When running ./a.out . or any subfolder, it works correctly.
But if I write ./a.out .. , it fails to open files...
~/Desktop/ls$ ./a.out ..
.. ->
fkdkfdjkfdkfjdfkdfjkdfjkdjkfdkjf -> can't read file fkdkfdjkfdkfjdfkdfjkdfjkdjkfdkjf!
ss -> can't read file ss!
ls -> can't read file ls!
. ->
tg -> can't read file tg!
./a.out /home/login/Desktop doesn't work either, but ./a.out /home/login/Desktop/ls/ display correctly the content of the current folder.
It looks like a.out can't open parents dir, but ls -l gives :
-rwxrwxr-x 1 hellomynameis hellomynameis 13360 nov. 25 09:56 a.out
Am I doing it the wrong way ?
Thanks !
Your lstat call is wrong. When you get a name from the opened directory, it is a relative name, so you need to convert it to a correct path to let lstat locate the file:
char path[...];
sprintf(path,"%s/%s",av[1],dir->d_name);
lstat(path,...);
The program a.out may has not permission to read all the files in that folder. Try to run a.out with root permission.
And, if you want to check the error, please print the errno to get the detail of error when the lstat function does not execute success.
Program:
#include<stdio.h>
#include<stdlib.h>
#include<dirent.h>
#include<sys/stat.h>
int main(int argc, char *argv[])
{
DIR *dp;
struct dirent *dirp;
struct stat stbuf;
if (argc != 2)
printf("usage: ls directory_name\n");
if ((dp = opendir(argv[1])) == NULL)
printf("can’t open %s", argv[1]);
while ((dirp = readdir(dp)) != NULL){
stat(dirp->d_name,&stbuf);
if(S_ISDIR(stbuf.st_mode)){
printf("Directory: ");
}
printf("%s\n",dirp->d_name);
}
closedir(dp);
exit(0);
}
Output:
$ ./a.out test/
dir3
d
c
Directory: .
Directory: a
Directory: ..
Directory: dir4
Directory: dir2
Directory: dir0
Directory: b
Directory: e
Directory: dir1
$
The following are the list of files that the directory "test" contains.
$ ls -F test/
a b c d dir0/ dir1/ dir2/ dir3/ dir4/ e
$
The expected output is, if the file is directory the output will be "Directory: dir1/". Else only the file name.
But, the output of the program is not as expected. Is the program contains any error?. Is there any let me know.
Thanks in Advance...
Lets break it down into steps:
You start your program from some directory. This directory will become the process current working directory (CWD).
You call opendir on the directory test. This is actually the directory test in the CWD.
You call readdir to get the first entry in the directory.
This first entry is the . directory, which is short for current directory, all directories have it.
You call stat with ., meaning you call stat on the CWD. It of course succeeds and stat fills in the structure with all the details of the CWD.
Next iteration you get the entry a and you call stat on that entry. However since there is no a in the CWD, the stat call fails. You however doesn't check for that, and uses the stat structure that was filled in from the previous call (from the successful stat of the . directory).
And so on...
You need to tell stat to look for the entries in the given directory instead of the CWD. This can basically be done in only two ways:
Format a string so that you prefix the entry with the directory you passed to opendir. E.g.
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/%s", argv[1] ,dirp->p_name);
if (stat(path, &stbuf) != -1)
{
// `stat` call succeeded, do something
}
After calling opendir, but before looping that directory, change the CWD:
// Opendir call etc...
chdir(argv[1]);
// Loop using readdir etc.
Alternatively, start the program from the directory you want to check:
$ cd test
$ ../a.out .
I need to work with directories in a C program... The directory will be a parameter, I need to check all the files in the folder, if one of these files is a folder, I need to access it, also, I need to go back in the directories, i.e. "." and "..".
I'm using the library <dirent.h>, and right now I only can show all the content in the folder where I'm running the program (i.e. program.c, folder1, etc.). If I use something like "Desktop" as the parameter, it will show the error saying:
Couldn't open the directory: No such file or directory.
Can you tell me how to do this?
#include <dirent.h>
#include <stdio.h>
int main(int argc, char *argv[]){
DIR *dp;
struct dirent *ep;
dp = opendir (argv[1]);
if (dp != NULL){
while (ep = readdir (dp))
puts (ep->d_name);
(void) closedir (dp);
perror ("Couldn't open the directory");
}
return 0;
}
Example:
xx#xfx gcc e.c -o hola
xx#xfx ~/Desktop $ ./hola ./
hola
10475718_921955664486299_2309306989404354546_n.jpg
practica 4.tar.gz
h.c
pro
two-type-of-programmers-funny-jokes.png
as.c
a.c
prac 4
hol
Pr.hs
ej.c
du.c
.
tar
thats-racist-gif-1.gif
pract.
pract.c
e.c
prac.c
pract.c
..
Lab files #2
ejer1.c
music.pls
Lab files #4
ho2.c
dir2
ejero1.c
t
Mate
Couldn't open the directory: Success
I have a directory name called dir. It contain following files in order
12.07.2013
13.07.2013
14.07.2013
15.07.2013
16.07.2013
17.07.2013
I wrote following C program to display all the files from the directory dir
code :
#include <stdio.h>
#include <string.h>
#include <dirent.h>
int main (int argc, char *argv[])
{
DIR *directory;
struct dirent *file;
directory = opendir (argv[1]);
if (directory != NULL){
while (file = readdir (directory))
printf("FILE : %s \n",file->d_name);
(void) closedir (directory);
}
else
printf("Not able to open the directory\n");
return 0;
}
Above code give the actual output as
FILE : 14.07.2013
FILE : 13.07.2013
FILE : 17.07.2013
FILE : .
FILE : 15.07.2013
FILE : ..
FILE : 12.07.2013
FILE : 16.07.2013
but i expected output in proper order like below
FILE : 12.07.2013
FILE : 13.07.2013
FILE : 14.07.2013
FILE : 15.07.2013
FILE : 16.07.2013
FILE : 17.07.2013
When i directly see the files in directory it arranges & diplaying the files in proper order.
Then why above C code not reading file in proper order, i mean randomly reading the file.
Working environment : Linux(ubuntu12.04), gcc compiler
Thanks
It's not randomly reading the files, it's just reading the directory listing in the order it's stored in the directory file itself. When you "directly see the files in directory", I presume that means you're using ls, but ls is sorting the results before output. If you want matching output you need to do the same.