由于 Linux 命令 cd 不能用作外部命令,是否可以编写我的 cd 版本(这不是 shell-builtin 命令)?
Since Linux command cd could not be used as external command, is it possible to write my version of cd (which is not a shell-builtin command)?
我对那个实现很好奇,可能涉及到一些底层细节,但我很想学习。
顺便说一句,我仍在寻找解决 cd 难题的方法,而无需重写该命令。如果您对此有任何巧妙的解决方案,请随时在此处post。
实际上,您无法编写自己的 chdir
外部命令。
更准确地说,你可以,但它对你没有帮助。问题是在 child 进程中所做的目录更改对 parent 进程的当前目录没有影响。当 shell 执行您的外部 chdir
命令时,chdir
命令会更改它自己的当前目录,但调用它的 shell 会保持原样。
如果您真的非常想做某事,您可以考虑编写一个 chdir
命令来更改目录,然后在新目录中执行 shell。如果您使用:
exec chdir /some/where
那么您原来的 shell 将在新的当前目录 (/some/where
) 中被替换为新的 shell。但这将是相当绝望的措施。请注意,如果您没有使用 exec
作为前缀,则每次更改目录时,您都会得到一个新的 shell。它是一种目录堆栈,但您必须在退出时依次退出每个 shell。如果您经常在文件系统中导航,那将变得非常烦人。
chdir.c
这是答案中概述的 chdir
命令的简单实现。
#include <stdlib.h>
#include <unistd.h>
#include "so-stderr.h"
static char def_shell[] = "/bin/sh";
int main(int argc, char **argv)
{
char **cmdv;
err_setarg0(argv[0]);
if (argc <= 1)
err_usage("directory [cmd [arg ...]]");
if (chdir(argv[1]) != 0)
err_syserr("failed to change directory to %s\n", argv[1]);
if (argc == 2)
{
static char *args[3] = { 0, "-i", (char *)0 };
char *shell = getenv("SHELL");
if (shell == 0)
shell = def_shell;
args[0] = shell;
cmdv = args;
}
else
cmdv = &argv[2];
execvp(cmdv[0], cmdv);
err_syserr("failed to execute %s\n", cmdv[0]);
/*NOTREACHED*/
return 0;
}
报错
唯一稍微复杂的部分是使用来自 so-stderr.h
和 so-stderr.c
的错误报告功能,因为它们不是标准的。它们只是我编写的函数,基本上我在所有程序中都将它们用于错误报告。
带有 so-
前缀的文件名是带有 header stderr.h
和源 stderr.c
(加上一些辅助文件,大约1000行,有注释,在更多的功能中提供了更复杂的接口,一般控制也更多)。这是 chdir
程序使用的函数的极大简化(但功能齐全)的形式。
so-stderr.c
#include "so-stderr.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char *argv0 = "**undefined**";
void err_setarg0(const char *arg0)
{
argv0 = arg0;
}
void err_usage(const char *usestr)
{
fprintf(stderr, "Usage: %s %s\n", argv0, usestr);
exit(EXIT_FAILURE);
}
void err_syserr(const char *fmt, ...)
{
int errnum = errno;
fprintf(stderr, "%s: ", argv0);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
exit(EXIT_FAILURE);
}
如果我想依赖 C11,三个函数中的两个将被标记为 noreturn
。这是主 stderr
包中支持的另一个额外细节。
so-stderr.h
#ifndef SO_STDERR_H_INCLUDED
#define SO_STDERR_H_INCLUDED
extern void err_setarg0(const char *arg0);
extern void err_syserr(const char *fmt, ...);
extern void err_usage(const char *usestr);
#endif /* SO_STDERR_H_INCLUDED */
我对那个实现很好奇,可能涉及到一些底层细节,但我很想学习。
顺便说一句,我仍在寻找解决 cd 难题的方法,而无需重写该命令。如果您对此有任何巧妙的解决方案,请随时在此处post。
实际上,您无法编写自己的 chdir
外部命令。
更准确地说,你可以,但它对你没有帮助。问题是在 child 进程中所做的目录更改对 parent 进程的当前目录没有影响。当 shell 执行您的外部 chdir
命令时,chdir
命令会更改它自己的当前目录,但调用它的 shell 会保持原样。
如果您真的非常想做某事,您可以考虑编写一个 chdir
命令来更改目录,然后在新目录中执行 shell。如果您使用:
exec chdir /some/where
那么您原来的 shell 将在新的当前目录 (/some/where
) 中被替换为新的 shell。但这将是相当绝望的措施。请注意,如果您没有使用 exec
作为前缀,则每次更改目录时,您都会得到一个新的 shell。它是一种目录堆栈,但您必须在退出时依次退出每个 shell。如果您经常在文件系统中导航,那将变得非常烦人。
chdir.c
这是答案中概述的 chdir
命令的简单实现。
#include <stdlib.h>
#include <unistd.h>
#include "so-stderr.h"
static char def_shell[] = "/bin/sh";
int main(int argc, char **argv)
{
char **cmdv;
err_setarg0(argv[0]);
if (argc <= 1)
err_usage("directory [cmd [arg ...]]");
if (chdir(argv[1]) != 0)
err_syserr("failed to change directory to %s\n", argv[1]);
if (argc == 2)
{
static char *args[3] = { 0, "-i", (char *)0 };
char *shell = getenv("SHELL");
if (shell == 0)
shell = def_shell;
args[0] = shell;
cmdv = args;
}
else
cmdv = &argv[2];
execvp(cmdv[0], cmdv);
err_syserr("failed to execute %s\n", cmdv[0]);
/*NOTREACHED*/
return 0;
}
报错
唯一稍微复杂的部分是使用来自 so-stderr.h
和 so-stderr.c
的错误报告功能,因为它们不是标准的。它们只是我编写的函数,基本上我在所有程序中都将它们用于错误报告。
带有 so-
前缀的文件名是带有 header stderr.h
和源 stderr.c
(加上一些辅助文件,大约1000行,有注释,在更多的功能中提供了更复杂的接口,一般控制也更多)。这是 chdir
程序使用的函数的极大简化(但功能齐全)的形式。
so-stderr.c
#include "so-stderr.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char *argv0 = "**undefined**";
void err_setarg0(const char *arg0)
{
argv0 = arg0;
}
void err_usage(const char *usestr)
{
fprintf(stderr, "Usage: %s %s\n", argv0, usestr);
exit(EXIT_FAILURE);
}
void err_syserr(const char *fmt, ...)
{
int errnum = errno;
fprintf(stderr, "%s: ", argv0);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
exit(EXIT_FAILURE);
}
如果我想依赖 C11,三个函数中的两个将被标记为 noreturn
。这是主 stderr
包中支持的另一个额外细节。
so-stderr.h
#ifndef SO_STDERR_H_INCLUDED
#define SO_STDERR_H_INCLUDED
extern void err_setarg0(const char *arg0);
extern void err_syserr(const char *fmt, ...);
extern void err_usage(const char *usestr);
#endif /* SO_STDERR_H_INCLUDED */