使用 C 处理 POSIX 中的目录
working with directories in POSIX with C
我会继续说这是 Linux class 介绍的家庭作业。如果我自己没有进行广泛的尝试,我不会发布它,而且由于我本学期是一名远程学生,所以我无法到校园接受辅导。我需要一些帮助来找出问题所在。
本质上,作业要求我们编写一个程序,其基本功能与 POSIX 中的 pwd
命令相同,以显示当前目录的绝对路径。我们将与 main 一起使用三个函数。我们也不要使用 getcwd
命令。我将列出它们及其用途
inum_to_filename
:接受三个参数(要翻译的索引节点号、指向写入名称的缓冲区的指针以及缓冲区的大小)。 Return没什么。它是:
- 打开当前目录,
- 读取第一个目录条目,
- 如果当前目录的inode与传入的inode匹配,复制name到buffer并return.
- 否则读取下一个目录条目并重复上一步。
filename_to_inum
:接受一个参数(char *
代表文件名)。它return是对应的inode号。它是:
- 从文件 inode 中读取信息到内存中的结构中。
- 如果有任何问题,显示相应的错误。
- Return 结构中的 inode 编号。
display_path
:接受一个参数(来自当前工作目录的 inode)。 return没什么。它是:
- 创建一个字符数组以用作目录名称的缓冲区。
- 使用
filename_to_inode
获取父目录的索引节点。
- 如果父inode等于当前inode,我们已经到达root,可以return.
- 否则,切换到父目录并使用
inum_to_filename
查找传递给函数的索引节点的名称。使用步骤 1 中的缓冲区来存储它。
- 递归调用
display_path
显示绝对路径。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
DIR *dir_ptr = opendir(".");
struct dirent *dirent_ptr = readdir(dir_ptr);
int counter = 0;
while (counter != 1) {
if (inode_arg == dirent_ptr->d_ino) {
strcat(pathBuffer, "/");
strcat(pathBuffer, dirent_ptr->d_name);
counter = counter + 1;
return;
} else {
dirent_ptr = readdir(dir_ptr);
}
}
closedir(dir_ptr);
}
int filename_to_inum (char *src) {
int res = 0;
struct stat info;
int result = stat(src, &info);
if (result != 0) {
fprintf(stderr, "Cannot stat ");
perror(src);
exit(EXIT_FAILURE);
} else {
res = info.st_ino;
}
return res;
}
void display_path (int ino_src) {
int bufSize = 4096;
char pathBuffer[bufSize];
int ino_prnt = filename_to_inum("..");
if (ino_src == ino_prnt) {
//print for test
inum_to_filename(ino_src, pathBuffer, bufSize);
printf("%s", pathBuffer);
return;
} else {
//print for test
chdir("..");
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("%s", pathBuffer);
}
}
int main (int argc, char *argv[]) {
int c_ino = filename_to_inum(".");
display_path(c_ino);
printf("\n");
}
截至目前,它正在显示“/./MyName”,其中 MyName 是我在服务器上的个人命名目录。这是我 运行 程序所在的目录。当使用 pwd I return “/home/MyName”。我不太确定我的下一步是什么来获得正确的绝对路径。
这是一个非常好的任务:)。
我阅读并尝试了您的代码,它几乎是正确的。有两个小问题导致了不正确的行为。
第一期
当 display_path
到达根文件夹时,您不需要调用 inum_to_filename
并打印文件夹的名称,因为您已经在上一次迭代中打印了路径的第一个文件夹。这可以防止您的代码在路径的开头显示“./”。
即if条件变为:
if (ino_src == ino_prnt) {
return;
} else {
chdir("..");
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("%s", pathBuffer);
}
第二期:
您没有正确初始化保存目录名称的缓冲区。这会导致显示随机值。要解决此问题,您可以使用 memset
将缓冲区的初始值设置为零。
void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
DIR *dir_ptr = opendir(".");
struct dirent *dirent_ptr = readdir(dir_ptr);
int counter = 0;
memset(pathBuffer, 0, size_arg);
while (counter != 1) {
...
}
closedir(dir_ptr);
}
完整代码工作:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
DIR *dir_ptr = opendir(".");
struct dirent *dirent_ptr = readdir(dir_ptr);
int counter = 0;
memset(pathBuffer, 0, size_arg);
while (counter != 1) {
if (inode_arg == dirent_ptr->d_ino) {
strcat(pathBuffer, "/");
strcat(pathBuffer, dirent_ptr->d_name);
counter = counter + 1;
return;
} else {
dirent_ptr = readdir(dir_ptr);
}
}
closedir(dir_ptr);
}
int filename_to_inum (char *src) {
int res = 0;
struct stat info;
int result = stat(src, &info);
if (result != 0) {
fprintf(stderr, "Cannot stat ");
perror(src);
exit(EXIT_FAILURE);
} else {
res = info.st_ino;
}
return res;
}
/*
- Create an array of characters to use as a buffer for the name of the directory.
- Get the inode for the parent directory using filename_to_inode.
- If the parent inode is equal to the current inode, we have reached root and can return.
- Otherwise, change to the parent directory and use inum_to_filename to find the name for
the inode that was passed into the function. Use the buffer from step 1 to store it.
- Recursively call display_path to display the absolute path.
*/
void display_path (int ino_src) {
int bufSize = 4096;
char pathBuffer[bufSize];
int ino_prnt = filename_to_inum("..");
if (ino_src == ino_prnt) {
return;
} else {
chdir("..");
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("%s", pathBuffer);
}
}
int main (int argc, char *argv[]) {
int c_ino = filename_to_inum(".");
display_path(c_ino);
printf("\n");
}
输出:
ubuntu@ubuntu-VirtualBox:~/dev$ vi pwd.c
ubuntu@ubuntu-VirtualBox:~/dev$ gcc pwd.c
ubuntu@ubuntu-VirtualBox:~/dev$ ./a.out
/home/ubuntu/dev
ubuntu@ubuntu-VirtualBox:~/dev$ pwd
/home/ubuntu/dev
ubuntu@ubuntu-VirtualBox:~/dev$
代码主要设置为按正确顺序一次打印一个名称,因此主要问题是使用 strcat()
而不是 strcpy()
。此外,在开始时检测您何时位于根目录中也很重要;如果你不这样做,当当前目录是根目录时,你可以得到 /.
或类似的东西(取决于你如何协调打印)。
这个版本的代码有:
压缩了 inum_to_filename()
中的循环,但也添加了错误报告。请记住,一个进程可以 运行 在一个它没有权限访问的目录中(它通常需要一个 setuid 程序——尽管权限可以在程序启动后更改)。在这种情况下,它可能无法打开 ..
(或 .
)。
丢失变量count
;它没有用处。使用分配和测试习惯用法允许代码包含对 readdir()
.
的单个调用
使用strcpy()
代替strcat()
。
使用类型ino_t
存储inode号。使用 size_t
作为尺寸。
减少filename_to_inum()
中的中间变量个数。
注意if (ino_src == ino_prnt)
语句体中的代码是针对根目录的;在没有测试打印的情况下,它什么也做不了。
注意else
部分的打印是操作的主要部分,不仅仅是测试打印。
错误检查chdir("..");
在 main()
中检测根目录。
观察到这段代码不适合直接改写成函数,因为它在成功时将进程的当前目录更改为/
。
修改后的代码:
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static void inum_to_filename(ino_t inode_arg, char *pathBuffer, size_t size_arg)
{
assert(size_arg > 0);
DIR *dir_ptr = opendir(".");
if (dir_ptr == 0)
{
fprintf(stderr, "Failed to open directory '.' (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
struct dirent *dirent_ptr;
while ((dirent_ptr = readdir(dir_ptr)) != 0)
{
if (inode_arg == dirent_ptr->d_ino)
{
if (strlen(dirent_ptr->d_name) >= size_arg)
{
fprintf(stderr, "File name %s too long (%zu vs %zu max)\n",
dirent_ptr->d_name, strlen(dirent_ptr->d_name), size_arg);
exit(EXIT_FAILURE);
}
strcpy(pathBuffer, dirent_ptr->d_name);
break;
}
}
closedir(dir_ptr);
}
static ino_t filename_to_inum(char *src)
{
struct stat info;
if (stat(src, &info) != 0)
{
fprintf(stderr, "Cannot stat ");
perror(src);
exit(EXIT_FAILURE);
}
return info.st_ino;
}
static void display_path(ino_t ino_src)
{
size_t bufSize = 4096;
char pathBuffer[bufSize];
ino_t ino_prnt = filename_to_inum("..");
if (ino_src == ino_prnt)
{
// print for test
inum_to_filename(ino_src, pathBuffer, bufSize);
printf("%s", "(root): /\n");
}
else
{
// print for real
if (chdir("..") != 0)
{
fprintf(stderr, "Failed to chdir to .. (%d: %s)\n",
errno, strerror(errno));
}
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("/%s", pathBuffer);
}
}
int main(void)
{
ino_t c_ino = filename_to_inum(".");
ino_t r_ino = filename_to_inum("/");
if (r_ino == c_ino)
putchar('/');
else
display_path(c_ino);
printf("\n");
}
毫无疑问,还有其他方法可以解决这个问题。
警告:这让我在 /Volumes/CRUZER/Sub-Directory
中工作时感到有些悲伤,这是一个记忆棒。扫描 /Volumes
时找不到索引节点(1
,这很奇怪),我也没有弄清楚为什么。我的一个程序——getpwd
实现——运行良好;另一个有不同的问题。我希望我能查个水落石出。使用 GCC 5.1.0.
在 Mac OS X 10.10.5 上进行测试
我会继续说这是 Linux class 介绍的家庭作业。如果我自己没有进行广泛的尝试,我不会发布它,而且由于我本学期是一名远程学生,所以我无法到校园接受辅导。我需要一些帮助来找出问题所在。
本质上,作业要求我们编写一个程序,其基本功能与 POSIX 中的 pwd
命令相同,以显示当前目录的绝对路径。我们将与 main 一起使用三个函数。我们也不要使用 getcwd
命令。我将列出它们及其用途
inum_to_filename
:接受三个参数(要翻译的索引节点号、指向写入名称的缓冲区的指针以及缓冲区的大小)。 Return没什么。它是:- 打开当前目录,
- 读取第一个目录条目,
- 如果当前目录的inode与传入的inode匹配,复制name到buffer并return.
- 否则读取下一个目录条目并重复上一步。
filename_to_inum
:接受一个参数(char *
代表文件名)。它return是对应的inode号。它是:- 从文件 inode 中读取信息到内存中的结构中。
- 如果有任何问题,显示相应的错误。
- Return 结构中的 inode 编号。
display_path
:接受一个参数(来自当前工作目录的 inode)。 return没什么。它是:- 创建一个字符数组以用作目录名称的缓冲区。
- 使用
filename_to_inode
获取父目录的索引节点。 - 如果父inode等于当前inode,我们已经到达root,可以return.
- 否则,切换到父目录并使用
inum_to_filename
查找传递给函数的索引节点的名称。使用步骤 1 中的缓冲区来存储它。 - 递归调用
display_path
显示绝对路径。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
DIR *dir_ptr = opendir(".");
struct dirent *dirent_ptr = readdir(dir_ptr);
int counter = 0;
while (counter != 1) {
if (inode_arg == dirent_ptr->d_ino) {
strcat(pathBuffer, "/");
strcat(pathBuffer, dirent_ptr->d_name);
counter = counter + 1;
return;
} else {
dirent_ptr = readdir(dir_ptr);
}
}
closedir(dir_ptr);
}
int filename_to_inum (char *src) {
int res = 0;
struct stat info;
int result = stat(src, &info);
if (result != 0) {
fprintf(stderr, "Cannot stat ");
perror(src);
exit(EXIT_FAILURE);
} else {
res = info.st_ino;
}
return res;
}
void display_path (int ino_src) {
int bufSize = 4096;
char pathBuffer[bufSize];
int ino_prnt = filename_to_inum("..");
if (ino_src == ino_prnt) {
//print for test
inum_to_filename(ino_src, pathBuffer, bufSize);
printf("%s", pathBuffer);
return;
} else {
//print for test
chdir("..");
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("%s", pathBuffer);
}
}
int main (int argc, char *argv[]) {
int c_ino = filename_to_inum(".");
display_path(c_ino);
printf("\n");
}
截至目前,它正在显示“/./MyName”,其中 MyName 是我在服务器上的个人命名目录。这是我 运行 程序所在的目录。当使用 pwd I return “/home/MyName”。我不太确定我的下一步是什么来获得正确的绝对路径。
这是一个非常好的任务:)。 我阅读并尝试了您的代码,它几乎是正确的。有两个小问题导致了不正确的行为。
第一期
当 display_path
到达根文件夹时,您不需要调用 inum_to_filename
并打印文件夹的名称,因为您已经在上一次迭代中打印了路径的第一个文件夹。这可以防止您的代码在路径的开头显示“./”。
即if条件变为:
if (ino_src == ino_prnt) {
return;
} else {
chdir("..");
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("%s", pathBuffer);
}
第二期:
您没有正确初始化保存目录名称的缓冲区。这会导致显示随机值。要解决此问题,您可以使用 memset
将缓冲区的初始值设置为零。
void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
DIR *dir_ptr = opendir(".");
struct dirent *dirent_ptr = readdir(dir_ptr);
int counter = 0;
memset(pathBuffer, 0, size_arg);
while (counter != 1) {
...
}
closedir(dir_ptr);
}
完整代码工作:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
DIR *dir_ptr = opendir(".");
struct dirent *dirent_ptr = readdir(dir_ptr);
int counter = 0;
memset(pathBuffer, 0, size_arg);
while (counter != 1) {
if (inode_arg == dirent_ptr->d_ino) {
strcat(pathBuffer, "/");
strcat(pathBuffer, dirent_ptr->d_name);
counter = counter + 1;
return;
} else {
dirent_ptr = readdir(dir_ptr);
}
}
closedir(dir_ptr);
}
int filename_to_inum (char *src) {
int res = 0;
struct stat info;
int result = stat(src, &info);
if (result != 0) {
fprintf(stderr, "Cannot stat ");
perror(src);
exit(EXIT_FAILURE);
} else {
res = info.st_ino;
}
return res;
}
/*
- Create an array of characters to use as a buffer for the name of the directory.
- Get the inode for the parent directory using filename_to_inode.
- If the parent inode is equal to the current inode, we have reached root and can return.
- Otherwise, change to the parent directory and use inum_to_filename to find the name for
the inode that was passed into the function. Use the buffer from step 1 to store it.
- Recursively call display_path to display the absolute path.
*/
void display_path (int ino_src) {
int bufSize = 4096;
char pathBuffer[bufSize];
int ino_prnt = filename_to_inum("..");
if (ino_src == ino_prnt) {
return;
} else {
chdir("..");
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("%s", pathBuffer);
}
}
int main (int argc, char *argv[]) {
int c_ino = filename_to_inum(".");
display_path(c_ino);
printf("\n");
}
输出:
ubuntu@ubuntu-VirtualBox:~/dev$ vi pwd.c
ubuntu@ubuntu-VirtualBox:~/dev$ gcc pwd.c
ubuntu@ubuntu-VirtualBox:~/dev$ ./a.out
/home/ubuntu/dev
ubuntu@ubuntu-VirtualBox:~/dev$ pwd
/home/ubuntu/dev
ubuntu@ubuntu-VirtualBox:~/dev$
代码主要设置为按正确顺序一次打印一个名称,因此主要问题是使用 strcat()
而不是 strcpy()
。此外,在开始时检测您何时位于根目录中也很重要;如果你不这样做,当当前目录是根目录时,你可以得到 /.
或类似的东西(取决于你如何协调打印)。
这个版本的代码有:
压缩了
inum_to_filename()
中的循环,但也添加了错误报告。请记住,一个进程可以 运行 在一个它没有权限访问的目录中(它通常需要一个 setuid 程序——尽管权限可以在程序启动后更改)。在这种情况下,它可能无法打开..
(或.
)。丢失变量
count
;它没有用处。使用分配和测试习惯用法允许代码包含对readdir()
. 的单个调用
使用
strcpy()
代替strcat()
。使用类型
ino_t
存储inode号。使用size_t
作为尺寸。减少
filename_to_inum()
中的中间变量个数。注意
if (ino_src == ino_prnt)
语句体中的代码是针对根目录的;在没有测试打印的情况下,它什么也做不了。注意
else
部分的打印是操作的主要部分,不仅仅是测试打印。错误检查
chdir("..");
在
main()
中检测根目录。观察到这段代码不适合直接改写成函数,因为它在成功时将进程的当前目录更改为
/
。
修改后的代码:
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static void inum_to_filename(ino_t inode_arg, char *pathBuffer, size_t size_arg)
{
assert(size_arg > 0);
DIR *dir_ptr = opendir(".");
if (dir_ptr == 0)
{
fprintf(stderr, "Failed to open directory '.' (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
struct dirent *dirent_ptr;
while ((dirent_ptr = readdir(dir_ptr)) != 0)
{
if (inode_arg == dirent_ptr->d_ino)
{
if (strlen(dirent_ptr->d_name) >= size_arg)
{
fprintf(stderr, "File name %s too long (%zu vs %zu max)\n",
dirent_ptr->d_name, strlen(dirent_ptr->d_name), size_arg);
exit(EXIT_FAILURE);
}
strcpy(pathBuffer, dirent_ptr->d_name);
break;
}
}
closedir(dir_ptr);
}
static ino_t filename_to_inum(char *src)
{
struct stat info;
if (stat(src, &info) != 0)
{
fprintf(stderr, "Cannot stat ");
perror(src);
exit(EXIT_FAILURE);
}
return info.st_ino;
}
static void display_path(ino_t ino_src)
{
size_t bufSize = 4096;
char pathBuffer[bufSize];
ino_t ino_prnt = filename_to_inum("..");
if (ino_src == ino_prnt)
{
// print for test
inum_to_filename(ino_src, pathBuffer, bufSize);
printf("%s", "(root): /\n");
}
else
{
// print for real
if (chdir("..") != 0)
{
fprintf(stderr, "Failed to chdir to .. (%d: %s)\n",
errno, strerror(errno));
}
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("/%s", pathBuffer);
}
}
int main(void)
{
ino_t c_ino = filename_to_inum(".");
ino_t r_ino = filename_to_inum("/");
if (r_ino == c_ino)
putchar('/');
else
display_path(c_ino);
printf("\n");
}
毫无疑问,还有其他方法可以解决这个问题。
警告:这让我在 /Volumes/CRUZER/Sub-Directory
中工作时感到有些悲伤,这是一个记忆棒。扫描 /Volumes
时找不到索引节点(1
,这很奇怪),我也没有弄清楚为什么。我的一个程序——getpwd
实现——运行良好;另一个有不同的问题。我希望我能查个水落石出。使用 GCC 5.1.0.