通过命令行用vscode打开test.txt, 能否省略后缀名? 能否传入基于非当前工作目录的相对路径?

新手上路,请多包涵

编程萌新, 非科班, 对终端用法没什么了解, 问了chatgpt, 结果反而越问越乱. 因此希望得到帮助解答, 感谢.

目前通过键入code test.txt来通过终端用vscode打开test.txt, 是没问题的. 但我想知道两个扩展问题:

  • 其一, 能否省略.txt, 亦即能否使用code test? 首先chatgpt说可以, 说vscode可以根据文件内容自动识别文件后缀, 并且notepad同理, 但经过试验发现无论vscode还是notepad都不行, 实际上只会打开一个无后缀的新文件test, 而非打开已存在的test.txt. 另外上网搜索, 又看到某帖子说要进入vscode修改一些软件设置选项才行, 但具体也是语焉不详.
  • 其二, 目前打开终端默认工作目录是用户的家目录, 所以若要打开一个位于其它位置的文件, 就要传入绝对路径, 比如code C:/playground/test.txt, 那我想请问能否在不先执行cd C:/playground的前提下直接使用code test, 问了chatgpt说再传入一个参数--folder-uri, 但试了也不好使, 也可能是我没用对. 另外也搜到一个关于notepad++的相关问题, 说要安装一个插件, 然后运行Perl脚本默认目录就是当前目录了(参见该贴), 所以我想vscode乃至notepad或者其它任意软件, 在传参时是不是都有机会不传入文件地址的绝对路径?

感谢回复, 做一点问题的补充.

对第一个问题, chatgpt是这么说的: 部分软件能够根据文件内容自动识别文件类型或者有默认的文件后缀, 它们会首先尝试打开指定的无后缀文件, 如果失败才会创建新的文件; 另外, 一些专用的软件如编译器、解释器、播放器等, 也可以在传参时省略文件后缀, 因为它们只处理特定类型的文件或者有预定义的规则来处理不同类型的文件. 例如gcc编译器可以使用不带后缀的源代码文件名作为参数, 它会根据源代码语言来选择合适的编译选项. 感觉说得还挺像回事的, 还有gcc为例, 虽然实际试了跟它说得不一样.
对第二个问题, --folder-uri这个参数是存在的, code --folder-uri会提示你需要传入一个非空值, 但我并没有找到相应文档, code --help也没有该参数的具体说明.

另外我再具体补充说明一下我的疑问, 在我看来这似乎本来应该是终端所具有的一个功能才对, 你看既然code test.txt能成功执行, 说明终端在自己的工作目录之外, 已经获悉了code.exe的所在路径, 那么按照我的预期, 最起码终端在自己的工作目录之外, 也应该尝试在code.exe同路径下寻找test.txt才对, 甚至完全可以允许用户在终端的设置里也搞一个类似环境变量PATH的东西, 从而方便用户给各个软件传参时传入文件路径. 这个功能在实现上显然是容易的, 但为啥没有这么做呢, 是有什么额外考量么?


你好, 能否再就终端的使用请教一下.
请允许我换个软件来举例. bbdown --config-file test.config, 这是一条调用bbdown并给它传入配置文件的命令, 前提需要配置文件与软件处于同一目录下.
但我发现, 如果终端当前工作目录并非bbdown软件所在目录, 那么执行这条命令就会出问题, 亦即找不到目标文件, 但这个错误又不是一条报错, 而是我根据程序的非预期行为而发现的.
那我之前提出的疑问是, 既然都从PATH里找到了bbdown.exe, 为什么就不能接着自动找到同目录下的test.config, 而非要传入绝对路径才行?
最近忽然有了思路, 或许终端是把test.config按当前工作目录(比如C:\)自动补全成了绝对路径C:\test.config, 然后再把它传给了bbdown. 如果是这样, 自然就不涉及根据PATH寻找test.config的问题, 同时也解释了终端为啥没报错, 因为补全路径这个活本身就不可能出错, 而补完的文件路径是否真实存在有效则是软件的事, 然后bbdown的开发者可能故意在读入错误配置文件路径时自动忽略了该错误, 所以最后给用户的使用体验就是传入错的文件路径并不会得到任何报错? 请问我这么理解对么?
或者我再把问题抽象一下, 就是当使用命令行调用某一程序并给它传参时, 若传入的参数是一个文件地址, 终端是否总是会自动将之补全为绝对路径再传给程序? 这是否是终端的一种通用处理方式? 对此也问了chatgpt,没有发现可靠资料, 还得到了相互冲突的回答, 所以还是得请教一下. 感谢.

阅读 1.7k
2 个回答

第一个问题,实际上会造成一些歧义,不可能去实现。这没法帮你去决定你是真的要打开一个文件,还是要创建一个文件,你给到程序的就应该是确切的文件路径。更何况大多数 Terminal 都提供了 TAB 来补全输入,这根本不是痛点,Windows 下常用的 Windows Terminal、Git Bash 和 CMD 都有提供。

第二个问题,可以使用 alias(别名)来解决。


看你补充的第一点, 你完全都把 ChatGPT 给问歪了,ChatGPT 在这里表达的是 “在打开没有后缀名的文件时,部分软件会自动推测文件的格式类型,从而选择正确的打开方式。”,而不是你所理解的,不提供后缀名,软件会自动检测打开指定 “可能” 的文件。

VS Code 强大的扩展能力肯定可以实现你想要的功能,那你可能就需要忍受,每次打开的时候,VS Code 就该询问你 “当前没有 xx 文件,但是有 xx.txt 文件?你是想要新建 xx 还是打开 xx.txt ?”。

当然也是可以用别名来实现的。

对于 folder-uri 这个选项确实是可用的,正如他的名字所说,是一个 uri,你需要按照 uri 的格式传递给他。

code --folder-uri file:///D:/Documents

这样就会使用 VS Code 打开 D:/Documents 目录,而实际上直接使用 code D:/Documents 也能达到一样的效果。

但是,如果你想

code --folder-uri file:///D:/Documents foo.txt

来打开 D:/Documents/foo.txt 那不行,VS Code 只会在当前工作目录(current working directory)来寻找 foo.txt。

最后,有没有一种可能,CWD(Current Working Directory)就是你说的那个特别的 “PATH”,你只需要使用 cd 来改变它。

我想,这个功能使用扩展也应该可以实现。

但是,就这两个功能而已,使用 alias 的成本更低。

终端是可以实现的,但是这个功能不符合常理,并且反直觉。


那我之前提出的疑问是, 既然都从PATH里找到了bbdown.exe, 为什么就不能接着自动找到同目录下的test.config, 而非要传入绝对路径才行?

我尝试写了一个终端,可以实现你说的这个功能

最近忽然有了思路, 或许终端是把test.config按当前工作目录(比如C:)自动补全成了绝对路径C:\test.config, 然后再把它传给了bbdown. 如果是这样, 自然就不涉及根据PATH寻找test.config的问题, 同时也解释了终端为啥没报错, 因为补全路径这个活本身就不可能出错, 而补完的文件路径是否真实存在有效则是软件的事, 然后bbdown的开发者可能故意在读入错误配置文件路径时自动忽略了该错误, 所以最后给用户的使用体验就是传入错的文件路径并不会得到任何报错? 请问我这么理解对么?

你可以理解把 test.config 理解成它是 ./test.config的简写,它已经是一个相对路径了。相对路径和绝对路径一样,也是可以指向唯一的一个文件的。

补全路径这个行为出错的可能性其实挺高的,而且是不可控的。特别是存在多个选择的情况下,会造成很多不必要的误解。

或者我再把问题抽象一下, 就是当使用命令行调用某一程序并给它传参时, 若传入的参数是一个文件地址, 终端是否总是会自动将之补全为绝对路径再传给程序? 这是否是终端的一种通用处理方式? 对此也问了chatgpt,没有发现可靠资料, 还得到了相互冲突的回答, 所以还是得请教一下. 感谢.

这样不合适,你没有办法确定程序是不是真的需要一个绝对路径。

比如说,现在有这样一个命令 vim -v bb.config ,然后运行的时候,假如 PATH下面有一个bb.config 文件,按照这个逻辑,命令行是不是就得给vim补全一个已经存在的绝对路径了呢?那么可能我只能输入完整路径才能打开当前目录的bb.config文件了。

这里正常的理解是,vim需要打开当前目录下的bb.config文件,而不是终端给我补全的目录,vim程序内部会去判断传入的bb.config是什么东西,它有自己的逻辑在。


这里终端自动补全,实现起来是没问题的,但是这不是一个终端该做的事情。

看下面这个例子:

这是一个名字叫做 bbsh 的终端,编译后可以在linux下运行。当在这个终端里运行 bbdown --config-file xxx 的时候,会自动把xxx补全为绝对路径。

bbsh.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>


extern char **environ;


char* findFileInPath(const char* filename) {
    const char* path = getenv("PATH"); // 获取PATH环境变量
    if (path == NULL) {
        return NULL;
    }

    char* pathCopy = strdup(path); // 复制PATH字符串,以便分割
    if (pathCopy == NULL) {
        return NULL;
    }

    char* token = strtok(pathCopy, ":"); // 使用冒号分割路径列表
    while (token != NULL) {
        // 构建文件路径
        char filePath[1024]; // 适当选择足够大的缓冲区
        snprintf(filePath, sizeof(filePath), "%s/%s", token, filename);

        // 检查文件是否存在
        if (access(filePath, F_OK) == 0) {
            free(pathCopy);
            return strdup(filePath); // 返回找到的文件路径
        }

        token = strtok(NULL, ":");
    }

    free(pathCopy);
    return strdup(filename); // 如果未找到文件,则返回文件名
}

int main() {
    char input[1024];
    char cwd[1024];
    getcwd(cwd, sizeof(cwd));
    printf("Wellcome to BBSH!\n");
    printf("cwd: %s\n", cwd);

    while (1) {
        printf("$ ");
        fgets(input, sizeof(input), stdin);

        // 去除换行符
        size_t len = strlen(input);
        if (len > 0 && input[len - 1] == '\n') {
            input[len - 1] = '\0';
        }

        if (strcmp(input, "exit") == 0) {
            break;
        }

        // 解析用户输入
        char *arg = strtok(input, " ");
        char *command = arg;
        char *args[30];
        int i = 0;
        while (arg != NULL) {
            args[i] = arg;
            i ++;
            arg = strtok(NULL, " ");
        }
        args[i] = NULL;
        if (command == NULL) {
            continue;  // 忽略空行
        }

        // 打印当前工作目录
        if (strcmp(command, "pwd") == 0) {
            printf("%s\n", cwd);
            continue;
        }

        // 打印当前环境变量
        if (strcmp(command, "export") == 0) {
            printf("%s\n", "export");
            for (char **env = environ; *env != NULL; env++) {
                printf("%s\n", *env);
            }
            continue;
        }

        //切换工作目录
        if (strcmp(command, "cd") == 0 && args[1] != NULL) {
            strcpy(cwd, args[1]);
            continue;
        }

        //对 bbdown 这个程序做特殊处理,自动转换test.config为完整路径。
        if (strcmp(command, "bbdown") == 0 && args[1] != NULL && strcmp(args[1], "--config-file") == 0 && args[2] != NULL) {
            printf("run: %s %s %s\n",command, args[1], args[2]);
            char* fullPath = findFileInPath(args[2]);
            args[2] = fullPath;
            printf("cover to:\n");
            printf("run: %s %s %s\n", command, args[1], args[2]);
        }

        pid_t pid = fork();

        if (pid == -1) {
            perror("fork");
            return 1;  // fork失败
        } else if (pid == 0) {
            // 子进程
            chdir(cwd);
            execvp(command, args);
            perror("execvp");
            exit(1);
        } else {
            // 父进程
            int status;
            waitpid(pid, &status, 0);
        }
    }

    return 0;
}

bbdown

#! /usr/bin/bash

echo "bbdown running"
echo "PWD" `pwd`
echo "\$0 $0"
echo "\$1 $1"
echo "\$2 $2"
echo "\$PATH" $PATH
echo "bbdown done."

运行后的效果:

image.png

logo
Microsoft
子站问答
访问
宣传栏