【问题标题】: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)?由于 Linux 命令 cd 不能用作外部命令,是否可以编写我的 cd 版本(这不是 shell-builtin 命令)?
【发布时间】:2015-08-29 00:14:22
【问题描述】:

我对那个实现很好奇,可能指的是一些底层细节,但我很想学习。

顺便说一句,我仍在寻找 cd 困境的解决方案,而无需重写该命令。如果您对此有任何巧妙的解决方案,请随时在此处发布。

【问题讨论】:

  • 问题是这样的:如果cd是一个外部命令,shell会产生一个新进程,调用your cd来改变密码,然后进程退出.更改的目录随进程消失,shell 保留在相同的密码中。衍生进程的副作用无法影响父 shell 进程。这并不是一个真正的困境:cd 必须 是一个 shell 内置命令。
  • 你想解决什么实际问题? chroot 有帮助吗?
  • 在 Solaris 9 上,实际上有一个 /usr/bin/cd 外部命令。它不会改变调用者的当前目录,但它可以用来测试你是否可以 改变到给定的目录(例如/usr/bin/cd /nosuchdir 失败)。我不相信它有用,但它就在那里。
  • @KeithThompson:Mac OS X 上也有/usr/bin/cd。我认为它的存在是为了满足 POSIX 的要求。 cd 不是 special built-in utilities 之一,因此它必须作为单独的二进制文件存在。

标签: linux shell operating-system


【解决方案1】:

实际上,您不能编写自己的chdir 外部命令。

更准确地说,你可以,但它对你没有帮助。麻烦的是子进程中所做的目录更改对父进程的当前目录没有影响。当 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.hso-stderr.c 的错误报告函数,因为它们不是标准的。它们只是我编写的函数,用于在我的所有程序中进行错误报告。

带有so- 前缀的文件名是更复杂的代码块的最小版本(40 多行没有 cmets),标题为stderr.h 和源代码stderr.c(加上一些辅助文件,大约 1000 行与 cmets,提供更复杂的接口,更多的功能,以及更多的控制)。这是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 编写一个shell 脚本,然后使用period 获取它。这将在同一个 shell 中运行而不会产生进程。我并不是说它是理想的,但它会起作用的哈哈
  • 嗯,我想是这样......这部分取决于你对“外部命令”的定义......但我知道你的评论大多是诙谐的。
  • 我知道 ;-)。最后的 LOL 表示我对使用源的解决方案的感受;-)。您可以为 cd 创建一个别名,作为新 cd 脚本的源代码。
  • @XupengTong:我会使用 C,尽管您可以更快地对其进行原型设计,也许,在脚本中。定义命令行界面:chdir directory [command [arg …]] 将是我在添加选项之前使用的。如果未指定命令,则运行(交互式)shell。然后这是一个直接的编程练习。你会怎么做?
猜你喜欢
  • 2023-02-01
  • 2021-10-12
  • 1970-01-01
  • 2020-02-26
  • 1970-01-01
  • 1970-01-01
  • 2021-12-19
  • 2022-01-25
  • 1970-01-01
相关资源
最近更新 更多