使用 Linux 命令行的人来说,pwd命令是非常有用的,执行 pwd 命令可立刻得知您目前所在的工作目录的绝对路径名称。

认识pwd命令

顾名思义,pwd命令打印当前工作目录或简单的目录用户,目前。 它使用从根 (/) 开始的完整路径打印当前目录名称。 此命令内置于 shell 命令中。

对于内置命令就是shell当做自带的,因为shell当中自己要进行管理,那么就需要一些命令进行管理,不同的shell肯定有不同的shell命令。

我们用type命令就可以看到其的类型,内置shell命令其实就是bash当中内部的命令,就好比我们一个软件内部的嵌套的不同的功能一样;

在这里插入图片描述

pwd 的基本语法:

pwd [OPTION]

选项 :

-L (logical) 目录连接链接时,输出连接路径
-P (physical) 输出物理路径

使用默认方式,用 pwd 命令查看默认工作目录的完整路径

在这里插入图片描述
使用pwd -P 显示出实际路径

在这里插入图片描述
目录连接链接时,pwd显示的是连接路径;pwd -P 显示出实际路径,而非使用连接(link)路径;

在这里插入图片描述

POSIX 要求默认值为 -L,但大多数脚本都需要 -P。当前外壳默认为 -L,而独立时
pwd 实现默认为 -P。

最简单的pwd命令实现

实现pwd真的太简单了,只要在程序中使用getcwd()函数就可以实现获得绝对路径的功能了。

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

int main(int argc, char **argv)
{
    char *wd =getcwd(NULL, 0);

    if (NULL == wd) {
        printf("Get cerrent working directory fail.\n");
        exit(-1);
    } else {
        printf("%s\n", wd);
    }

    return 0;
}

编译运行:

在这里插入图片描述

上面其实已经实现了pwd的功能,但这显然只能实现其功能,而不能了解其工作原理;我们接着往下来!

快速理解inode

在一个文件系统中,一个inode代表一个文件,并使用一个整数值来表示该inode,里面包含了与该文件有关的一些信息。

inode包含很多的文件元信息,但不包含文件名,利用stat命令可以查看一个文件更加详细的inode信息,包括inode编号,占用的块数量,块大小,硬链接个数,atime, mtime, ctime

在这里插入图片描述

总之,除了文件名以外的所有文件信息,都存在inode之中。

Linux中的目录

Linux文件系统是目录和文件组成的一种层次结构,目录的起点称为根(root),其名字是一个字符 / 。

在作为路径使用时, 根目录 / 是一个绝对路径,而Linux中也有一些相对路径可用,比如 . 或 ./ 表示当前目录、 … 或 …/ 表示上一级目录、 ~ 或 ~/ 表示当前用户的主目录(家目录)。

在Linux目录中,每个名字有一个inode number,inode number指出了存储数据的硬盘空间的位置。通过ls -i看到名字和inode对应关系。

在这里插入图片描述

综上,Linux中,一个文件(包括目录)的文件名,及文件名与inode的对应关系,都是由包含该文件的目录所描述的。

pwd实现

static void file_name_free(struct file_name *p)
{
	free (p->buf);
	free (p);
}

static struct file_name *file_name_init (void)
{
	struct file_name *p = (struct file_name *)malloc (sizeof *p);

	/* 从大于 PATH_MAX 的缓冲区开始,但要注意系统
其中 PATH_MAX 非常大——例如,INT_MAX。  */
	p->n_alloc = MIN (2 * PATH_MAX, 32 * 1024);

	p->buf = (char *)malloc (p->n_alloc);
	p->start = p->buf + (p->n_alloc - 1);
	p->start[0] = '\0';
	return p;
}



/* 将长度为 S_LEN 的名称 S 附加到不断增长的文件名 P 之前。  */
static void file_name_prepend (struct file_name *p, char const *s, size_t s_len)
{
	size_t n_free = p->start - p->buf;
	if (n_free < 1 + s_len)
	{
		size_t half = p->n_alloc + 1 + s_len;
		char *q = (char *)calloc (2, half);
		size_t n_used = p->n_alloc - n_free;
		p->start = q + 2 * half - n_used;
		memcpy (p->start, p->buf + n_free, n_used);
		free (p->buf);
		p->buf = q;
		p->n_alloc = 2 * half;
	}

	p->start -= 1 + s_len;
	p->start[0] = '/';
	memcpy (p->start + 1, s, s_len);
}

/* 返回一个由 N 个 '/' 分隔的“..”组件组成的字符串(malloc'd)。  */
static char *nth_parent(size_t n)
{
	char *buf = (char *)calloc(3, n);
	char *p = buf;

	for (size_t i = 0; i < n; i++)
	{
		memcpy (p, "../", 3);
		p += 3;
	}
	p[-1] = '\0';
	return buf;
}


static inline bool dot_or_dotdot (char const *file_name)
{
	if (file_name[0] == '.')
	{
		char sep = file_name[(file_name[1] == '.') + 1];
		return (! sep || ISSLASH (sep));
	}
	else
		return false;
}

/* readdir 的包装器,以便调用者看不到 '.' 的条目。 或“……”。  */
static inline struct dirent const *readdir_ignoring_dot_and_dotdot (DIR *dirp)
{
	while (1)
	{
		struct dirent const *dp = readdir(dirp);
		if (dp == NULL || ! dot_or_dotdot(dp->d_name))
			return dp;
	}
}


static void find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
                size_t parent_height)
{
	DIR *dirp;
	int fd;
	struct stat parent_sb;
	bool use_lstat;
	bool found;

	dirp = opendir("..");
	if (dirp == NULL)
		printf("cannot open directory %s\n",strdup(nth_parent(parent_height)));

	fd = dirfd (dirp);
/*chdir()这个系统调用,是改变当前程序的工作目录,不是改变bash的工作目录*/	
	if ((0 <= fd ? fchdir(fd) : chdir("..")) < 0)
		printf("failed to chdir to %s\n",strdup(nth_parent(parent_height)));

	if ((0 <= fd ? fstat(fd, &parent_sb) : stat(".", &parent_sb)) < 0)
		printf("failed to stat %s\n",strdup(nth_parent(parent_height)));


	/*如果父目录和子目录在不同的设备上,那么我们
不能依赖 d_ino 来获取有用的 i-node 编号; 请改用 lstat。  */
	use_lstat = (parent_sb.st_dev != dot_sb->st_dev);

	found = false;
	while (1)
	{
		struct dirent const *dp;
		struct stat ent_sb;
		unsigned long long int ino = 0;

		errno = 0;
		if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL)
		{
			if (errno)
			{
				/* 在 closedir 调用中保存/恢复 errno。   */
				int e = errno;
				closedir (dirp);
				errno = e;

				/* 安排在退出此循环后进行诊断。   */
				dirp = NULL;
			}
			break;
		}

		ino = dp->d_ino;

		if (ino == NOT_AN_INODE_NUMBER || use_lstat)
		{
			if (lstat (dp->d_name, &ent_sb) < 0)
			{
				/* 跳过我们无法统计的任何条目。  */
				continue;
			}
			ino = ent_sb.st_ino;
		}

		if (ino != dot_sb->st_ino)
			continue;

		/* 如果我们不跨越设备边界,那么一个简单的 i-node
匹配就足够了。  */
		if ( ! use_lstat || ent_sb.st_dev == dot_sb->st_dev)
		{
			file_name_prepend(file_name, dp->d_name, strlen(dp->d_name));
			found = true;
			break;
		}
	}

		if (dirp == NULL || closedir (dirp) != 0)
		{
			/* N请注意,此诊断适用于 readdir
和关闭失败。  */
			printf("reading directory %s\n",strdup(nth_parent(parent_height)));
		}

		if ( ! found)
			printf("couldn't find directory entry in %s with matching i-node",strdup(nth_parent(parent_height)));;

		*dot_sb = parent_sb;
}


/* 调用 lstat 以获取“/”的设备和 inode 编号。
    失败时,返回NULL。 否则,设置成员
    *ROOT_D_I 相应地并返回 ROOT_D_I。  */
struct dev_ino *get_root_dev_ino (struct dev_ino *root_d_i)
{
	struct stat statbuf;
	if (lstat ("/", &statbuf))
		return NULL;
	root_d_i->st_ino = statbuf.st_ino;
	root_d_i->st_dev = statbuf.st_dev;
	return root_d_i;
}


static void robust_getcwd (struct file_name *file_name)
{
	size_t height = 1;
	struct dev_ino dev_ino_buf;
	struct dev_ino *root_dev_ino = get_root_dev_ino (&dev_ino_buf);
	struct stat dot_sb;

	if (root_dev_ino == NULL)
		printf("failed to get attributes of %s\n",strdup("/"));

	if (stat (".", &dot_sb) < 0)
		printf("failed to stat %s\n",strdup("."));

	while (1)
	{
		find_dir_entry (&dot_sb, file_name, height++);
	}

	/* 查看是否需要前导斜线; file_name_prepend 添加一个。   */
	if (file_name->start[0] == '\0')
		file_name_prepend (file_name, "", 0);
}

/* 如果“pwd -L”可接受,则从环境中返回 PWD
    输出,否则为 NULL。   */
static char *logical_getcwd (void)
{
	struct stat st1;
	struct stat st2;
	char *wd = getenv("PWD");
	char *p;

	/* 首先进行文本验证。  */
	if (!wd || wd[0] != '/')
		return NULL;
	
	p = wd;
	while ((p = strstr(p, "/.")))
	{
		if (!p[2] || p[2] == '/'
		  || (p[2] == '.' && (!p[3] || p[3] == '/')))
			return NULL;
			
		p++;
	}


	/* 系统调用验证  */
	if (stat(wd, &st1) == 0 && stat (".", &st2) == 0)
		return wd;
	
	return NULL;
}


int main(int argc, char **argv)
{
	
	char *wd;
	/* POSIX 要求默认值为 -L,但大多数脚本都需要 -P。
当前外壳默认为 -L,而独立时
pwd 实现默认为 -P。 */
	
	bool logical = (getenv ("POSIXLY_CORRECT") != NULL);
	
	while (1)
	{
		int c = getopt(argc, argv, "LP");
		
		if (c == -1)
			break;
		switch (c)
		{
			case 'L':
				logical = true;
				break;
			case 'P':
				logical = false;
				break;
			default:
				break;
		}
	}

	if (optind < argc)
		printf("ignoring non-option arguments\n");

	if(logical)
	{
		wd = logical_getcwd();
		if (wd)
		{
			puts(wd);
			return 0;
		}
	}

	wd = getcwd(NULL, 0);
	if (wd != NULL)
	{
		puts(wd);
		free(wd);
	}
	else
	{
		struct file_name *file_name = file_name_init();
		robust_getcwd(file_name);
		puts (file_name->start);
		file_name_free(file_name);
	}
	
	return 0;
}


编译运行

在这里插入图片描述

总结

Linux中用 pwd 命令来查看”当前工作目录“的完整路径。 简单得说,每当你在终端进行操作时,你都会有一个当前工作目录。在不太确定当前位置时,就会使用pwd来判定当前目录在文件系统内的确切位置。

欢迎关注微信公众号【程序猿编码】,欢迎添加本人微信号(17865354792)交流学习。


版权声明:本文为chen1415886044原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/chen1415886044/article/details/119846484