how to determine directory or file in linux in c
I checked that Directory names ends with ‘/’ character. So, I checked that if there are ‘/’ character at the end of name, don’t print that name but when I run the function, all of them is printed in selected directory? Can you lead me that how can I check the end of Directory name?
how about try checking if the name has an extension. Most files will have some extension like .pdf .c etc
Directory names don’t end with a slash (and you’re accessing the null terminator anyway). The slash is added by the ls command (given certain options). You can try using the d_type field (will not work on all systems) or you can do an lstat on the name. d_type should be DT_DIR for a directory.
3 Answers 3
What you are looking for is stat or one of its variants. Specifically look at the st_mode field of struct stat . The macro you are interested in is S_ISDIR(x) .
Find below your modified code that demonstrates what you want:
void list_file(char* directory) < DIR *d; struct dirent *dir; int dir_len = strlen(directory); char* path = malloc(dir_len + NAME_MAX + 2); // +2, 1 for '/' and 1 for '\0' if(path == NULL) < fprintf(stderr, "malloc failed\n"); return; >strcpy(path, directory); if(path[dir_len-1] != '/') < path[dir_len] = '/'; dir_len++; >d = opendir(directory); if (d) < while ((dir = readdir(d)) != NULL) < struct stat buf; strcpy(&path[dir_len], dir->d_name); if(stat(path, &buf) < 0) < fprintf(stderr, "error\n"); >else d_name); > > > closedir(d); > free(path); >
I have removed your first print as it was printing the null terminating character of the string.
As pointed out in the comments since we are dealing with Linux you can use the d_type field in struct dirent (which is not part of POSIX but is part of Linux).
With that said the code would be the following.
void list_file(char* directory)< DIR *d; struct dirent *dir; d = opendir(directory); if (d) < while ((dir = readdir(d)) != NULL) < struct stat buf; if(dir->d_type == DT_DIR) < printf("%s\n", dir->d_name); > > closedir(d); > >
It is a lot cleaner, no need for malloc .
That will not work unless directory is «.» — the current directory. Per linux.die.net/man/3/readdir, the d_name member of a struct dirent is just the base file name and not the entire path that would include the preceding directories — it’s merely «filename» and not «/full/path/to/filename».
@AndrewHenle: Well, if you’ve got the file descriptor of the directory, you can fstatat() , which is much nicer to work with.
Try using either of stat, fstat, lstat as required. This is used to get the file status.
int stat(const char *path, struct stat *buf); int fstat(int fd, struct stat *buf); int lstat(const char *path, struct stat *buf);
- stat() stats the file pointed to by path and fills in buf.
- lstat() is identical to stat(), except that if path is a symbolic link, then the link itself is stat-ed, not the file that it refers to.
- fstat() is identical to stat(), except that the file to be stat-ed is specified by the file descriptor fd.
All of them return a stat structure, which contains the following fields:
Compare the value with S_IFDIR to check if it is a directory.
For more refer to : man 2 stat
Using the struct stat can also help you because it contains many different information of a file.
On Linux, the dirent structure is defined as follows: struct dirent < ino_t d_ino; /* inode number */ off_t d_off; /* not an offset; see NOTES */ unsigned short d_reclen; /* length of this record */ unsigned char d_type; /* type of file; not supported by all filesystem types */ char d_name[256]; /* filename */ >; [. ] Other than Linux, the d_type field is available mainly only on BSD sys‐ tems. This field makes it possible to avoid the expense of calling lstat(2) if further actions depend on the type of the file. If the _BSD_SOURCE feature test macro is defined, then glibc defines the fol‐ lowing macro constants for the value returned in d_type: DT_BLK This is a block device. DT_CHR This is a character device. DT_DIR This is a directory. DT_FIFO This is a named pipe (FIFO). DT_LNK This is a symbolic link. DT_REG This is a regular file. DT_SOCK This is a UNIX domain socket. DT_UNKNOWN The file type is unknown.
Locating special folders in cross-platform .NET applications
.NET has APIs for locating special folders that can be used for application and user configuration and data storage. They provide a convenient, portable way to make cross-platform applications find the appropriate folders on different operating systems. We’ll look at how Environment.GetFolderPath , Path.GetTempPath , and Path.GetTempFileName behave on Linux.
Environment.GetFolderPath
The System.Environment class has two GetFolderPath overloads:
public static string GetFolderPath (SpecialFolder folder); public static string GetFolderPath (SpecialFolder folder, SpecialFolderOption option);
SpecialFolder is an enum with values like ApplicationData , MyDocuments , and ProgramFiles . The SpecialFolderOption enum has three values: None , Create , and DoNotVerify . These control the return value when the folder does not exist. Specifying None causes an empty string to be returned. Specifying Create causes the folder to be created. And DoNotVerify causes the path to be returned even when the folder does not exist.
Note that SpecialFolder and SpecialFolderOption are nested in the Environment class, and to use them directly you should add a using static System.Environment; statement to your code.
To make this API work cross-platform, .NET Core needs to map the SpecialFolder values to some locations. For Linux, this mapping is based on file-hierarcy and basedir-spec.
SpecialFolder | Environment Variable | Config File | Linux Default |
---|---|---|---|
CommonApplicationData | /usr/share | ||
CommonTemplates | /usr/share/templates | ||
MyDocuments (home) | HOME | passwd | / |
UserProfile | home | ||
ApplicationData | XDG_CONFIG_HOME | home/.config | |
LocalApplicationData | XDG_DATA_HOME | home/.local/share | |
Fonts | home/.fonts | ||
Desktop, DesktopDirectory | XDG_DESKTOP_DIR | user-dirs.dirs | home/Desktop |
Templates | XDG_TEMPLATES_DIR | user-dirs.dirs | home/Templates |
MyVideos | XDG_VIDEOS_DIR | user-dirs.dirs | home/Videos |
MyMusic | XDG_MUSIC_DIR | user-dirs.dirs | home/Music |
MyPictures | XDG_PICTURES_DIR | user-dirs.dirs | home/Pictures |
The table lists all the mapped values in .NET Core 2.1. Other values are not mapped and return an empty string. The returned value is determined from left to right: first checking the environment variable, then falling back to the config file, and finally falling back to a default.
Cross-platform applications should be limited to using the mapped values, or they should be able to fall back to another location when GetFolderPath returns an empty string.
The user home folder is read from the HOME environment variable. When that is unset, the home directory is read from the system user database. It’s safe to assume that for known users, .NET Core will be able to determine the home directory. A number of other locations are based on the user home folder. Some can be overridden using environment variables and some others by using a file at ApplicationData/users-dirs.dirs .
On Windows, most of the special folders will exist by default. This may not be the case on Linux. It is the application’s responsibility to create the folder when it doesn’t exist. This may require some changes to your code to use the overload with a SpecialFolderOption .
For example, the following code ensures the LocalApplicationData folder will be created if it doesn’t exist.
// Use DoNotVerify in case LocalApplicationData doesn’t exist. string appData = Path.Combine(Environment.GetFolderPath(SpecialFolder.LocalApplicationData, SpecialFolderOption.DoNotVerify), "myapp"); // Ensure the directory and all its parents exist. Directory.CreateDirectory(appData);
Path.GetTempPath and Temp.GetTempFileName
The System.IO.Path class has a method that returns the path of the current user’s temporary folder:
public static string GetTempPath ();
Windows applications may assume the path returned here is user-specific. This is because the implementation picks up the USERPROFILE environment variable. When the variable is unset, the API returns the Windows temp folder.
On Linux, the implementation returns /tmp . This folder is shared with other users. As a consequence, applications should use unique names to avoid conflicts with other applications. Furthermore, because the location is shared, other users will be able to read the files created here, so you should not store sensitive data in this folder. The first user to create a file or directory will own it. This can cause your application to fail when trying to create a file or directory that is already owned by another user.
The Temp.GetTempFileName method solves these issues for creating files. It creates a unique file under GetTempPath that is only readable and writable by the current user.
On Windows, the value returned by GetTempPath can be controlled using the TMP / TEMP environment variables. On Linux, this can be done using TMPDIR .
On systems with systemd , like Fedora and Red Hat Enterprise Linux (RHEL), a user-private temporary directory is available and can be located using the XDG_RUNTIME_DIR environment variable.
Conclusion
In this article, you’ve seen the features and limitations of using Environment.GetFolderPath , Temp.GetTempPath and Temp.GetTempFileName in your cross-platform .NET Core applications.
Here are some additional .NET Core articles that might be helpful:
Last updated: September 3, 2019
How do I get the current executing directory on linux with .Net Core?
I need to load in an XML schema file to validate some information on a .Net Core 2.1 api that is on a linux server. Unfortunately, I do not have access to this server (we use jenkins to deploy so I have 0 contact with it), so I can only test on my computer which is Windows 10.
I have tried the following:
System.AppContext.BaseDirectory System.AppContext.BaseDirectory.Substring(0, AppContext.BaseDirectory.IndexOf("bin")); AppDomain.CurrentDomain.BaseDirectory Directory.GetCurrentDirectory() GetType().Assembly.Location System.Reflection.Assembly.GetExecutingAssembly().Location System.Reflection.Assembly.GetExecutingAssembly().CodeBase
All of these return the current execution location on Windows (i.e. C:/SomePath/SomeProject/Name/api.dll ) which I can use with Path.Combine to produce the path to the schema file. However, on linux, these all return /home/app/ which is not where the dll should be according to the Jenkins logs. This is leading to failures loading the schema file. The project is actually located under /services/projectname/ . Test Code:
var schema = new XmlSchemaSet < XmlResolver = new XmlUrlResolver() >; schema.Add(null, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Schema/schema.xsd"));
Expected: On Windows and Linux this loads the schema file using the .dll execution path as the base. Actual: On Linux I get home/app instead of the correct path. Edit: I cannot hardcode the path. The path changes on every deployment as the project name is versioned. This means the second I deploy it, any hardcoded value will be incorrect. I absolutely require a relative path. Beyond that technical requirement, hard coding is a major taboo. I will never get it past a code review.