AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 430318
Accepted
iBug
iBug
Asked: 2018-03-15 22:10:17 +0800 CST2018-03-15 22:10:17 +0800 CST 2018-03-15 22:10:17 +0800 CST

程序是否可以在 POSIX 中获取命令行参数之间的空格数?

  • 772

假设我使用以下行编写了一个程序:

int main(int argc, char** argv)

现在它通过检查argv.

程序可以检测参数之间有多少空格吗?就像我在 bash 中输入这些:

ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog       aaa      bbb

环境是现代 Linux(如 Ubuntu 16.04),但我想答案应该适用于任何符合 POSIX 的系统。

shell command-line
  • 6 6 个回答
  • 4703 Views

6 个回答

  • Voted
  1. Hans-Martin Mosner
    2018-03-15T22:16:19+08:002018-03-15T22:16:19+08:00

    一般来说,没有。命令行解析由 shell 完成,它不会使未解析的行对被调用程序可用。实际上,您的程序可能是从另一个创建 argv 的程序执行的,该程序不是通过解析字符串,而是通过以编程方式构造参数数组。

    • 58
  2. Best Answer
    Toby Speight
    2018-03-16T01:39:12+08:002018-03-16T01:39:12+08:00

    谈论“论点之间的空格”是没有意义的;这是一个外壳概念。

    shell 的工作是获取整行输入并将它们组成参数数组以启动命令。这可能涉及解析带引号的字符串、扩展变量、文件通配符和波浪号表达式等等。该命令以标准exec系统调用开始,该调用接受字符串向量。

    存在其他方法来创建字符串向量。许多程序通过预先确定的命令调用来分叉和执行它们自己的子进程——在这种情况下,永远不会有“命令行”这样的东西。类似地,当用户拖动文件图标并将其放在命令小部件上时,图形(桌面)外壳可能会启动一个进程 - 同样,没有文本行可以在“参数之间”包含字符。

    就调用的命令而言,shell 或其他父/前体进程中发生的事情是私有的和隐藏的——我们只看到标准 C 指定main()可以接受的字符串数组。

    • 39
  3. Kusalananda
    2018-03-15T22:56:29+08:002018-03-15T22:56:29+08:00

    不,这是不可能的,除非空格是参数的一部分。

    该命令访问数组中的各个参数(以一种或另一种形式,取决于编程语言),并且实际的命令行可以保存到历史文件中(如果在具有历史文件的 shell 中的交互式提示符下键入),但是从未以任何形式传递给命令。

    Unix 上的所有命令最终都由exec()函数家族之一执行。它们采用命令名称和参数列表或数组。它们都没有在 shell 提示符下键入命令行。该system()函数确实如此,但它的字符串参数稍后由 执行execve(),它再次采用参数数组而不是命令行字符串。

    • 16
  4. Basile Starynkevitch
    2018-03-16T12:06:00+08:002018-03-16T12:06:00+08:00

    一般来说,这是不可能的,就像其他几个答案所解释的那样。

    但是,Unix shell是普通程序(它们解释命令行并对其进行通配,即在执行&之前扩展命令)。请参阅有关shell 操作的说明。您可以编写自己的 shell(或者您可以修补一些现有的免费软件shell,例如GNU bash)并将其用作您的 shell(甚至是您的登录 shell,参见passwd(5)和shells(5))。forkexecvebash

    例如,您可能有自己的 shell 程序将完整的命令行放在某个环境变量中(MY_COMMAND_LINE例如想象一下)-或使用任何其他类型的进程间通信将命令行从 shell 传输到子进程-。

    我不明白你为什么要这样做,但你可能会编写一个以这种方式运行的 shell(但我建议不要这样做)。

    顺便说一句,一个程序可以由一些不是shell 的程序启动(但它执行fork(2)然后execve(2),或者只是execve在其当前进程中启动一个程序)。在那种情况下,根本没有命令行,您的程序可以在没有命令的情况下启动......

    请注意,您可能有一些(专门的)Linux 系统没有安装任何 shell。这是奇怪和不寻常的,但有可能。然后,您需要编写一个专门的init程序,根据需要启动其他程序 - 不使用任何 shell,而是通过执行fork& execve系统调用。

    另请阅读操作系统:三个简单的部分,不要忘记这execve实际上总是一个系统调用(在 Linux 上,它们在syscalls(2)中列出,另请参见intro(2))重新初始化虚拟地址空间(以及其他一些事情)的过程。

    • 9
  5. Stéphane Chazelas
    2018-03-18T01:20:59+08:002018-03-18T01:20:59+08:00

    你总是可以告诉你的 shell 告诉应用程序是什么 shell 代码导致它们执行。例如,通过使用钩子在环境变量zsh中传递该信息(作为示例,您将在程序中使用):$SHELL_CODEpreexec()printenvgetenv("SHELL_CODE")

    $ preexec() export SHELL_CODE=$1
    $ printenv SHELL_CODE
    printenv SHELL_CODE
    $ printenv  SHELL_CODE
    printenv  CODE
    $ $(echo printenv SHELL_CODE)
    $(echo printenv SHELL_CODE)
    $ for i in SHELL_CODE; do printenv "$i"; done
    for i in SHELL_CODE; do printenv "$i"; done
    $ printenv SHELL_CODE; : other command
    printenv SHELL_CODE; : other command
    $ f() printenv SHELL_CODE
    $ f
    f
    

    所有这些都将执行printenv为:

    execve("/usr/bin/printenv", ["printenv", "SHELL_CODE"], 
           ["PATH=...", ..., "SHELL_CODE=..."]);
    

    允许使用这些参数printenv检索导致执行的 zsh 代码。printenv我不清楚你想用这些信息做什么。

    使用'sbash时,最接近zsh's的功能preexec()将$BASH_COMMAND在DEBUG陷阱中使用它,但请注意,bash它会在其中进行某种程度的重写(特别是重构一些用作分隔符的空格),并且适用于每个(嗯,一些)命令运行,而不是在提示符下输入的整个命令行(另请参见functrace选项)。

    $ trap 'export SHELL_CODE="$BASH_COMMAND"' DEBUG
    $ printenv SHELL_CODE
    printenv SHELL_CODE
    $ printenv $(echo 'SHELL_CODE')
    printenv $(echo 'SHELL_CODE')
    $ for i in SHELL_CODE; do printenv "$i"; done; : other command
    printenv "$i"
    $ printf '%s\n' "$(printenv "SHELL_CODE")"
    printf '%s\n' "$(printenv "SHELL_CODE")"
    $ set -o functrace
    $ printf '%s\n' "$(printenv "SHELL_CODE")"
    printenv "SHELL_CODE"
    $ print${-+env  }    $(echo     'SHELL_CODE')
    print${-+env  } $(echo     'SHELL_CODE')
    

    看看 shell 语言语法中的一些分隔符是如何被压缩到 1 中的,以及如何不总是将完整的命令行传递给命令。所以在你的情况下可能没用。

    请注意,我不建议您这样做,因为您可能会将敏感信息泄露给每个命令,如下所示:

    echo very_secret | wc -c | untrustedcmd
    

    会将这个秘密泄露给wc和untrustedcmd。

    当然,除了 shell 之外,你也可以对其他语言做这种事情。例如,在 C 中,您可以使用一些宏将执行命令的 C 代码导出到环境中:

    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    #define WRAP(x) (setenv("C_CODE", #x, 1), x)
    
    int main(int argc, char *argv[])
    {
      if (!fork()) WRAP(execlp("printenv", "printenv", "C_CODE", NULL));
      wait(NULL);
      if (!fork()) WRAP(0 + execlp("printenv",   "printenv", "C_CODE", NULL));
      wait(NULL);
      if (argc > 1 && !fork()) WRAP(execvp(argv[1], &argv[1]));
      wait(NULL);
      return 0;
    }
    

    例子:

    $ ./a.out printenv C_CODE
    execlp("printenv", "printenv", "C_CODE", NULL)
    0 + execlp("printenv", "printenv", "C_CODE", NULL)
    execvp(argv[1], &argv[1])
    

    看看 C 预处理器如何压缩一些空格,就像在 bash 案例中一样。在大多数(如果不是所有)语言中,分隔符中使用的空间量没有区别,因此编译器/解释器在这里对它们采取一些自由也就不足为奇了。

    • 3
  6. ctrl-alt-delor
    2018-03-19T01:17:17+08:002018-03-19T01:17:17+08:00

    我将添加其他答案中缺少的内容。

    不

    查看其他答案

    也许,有点

    在程序中没有什么可以做的,但是当你运行程序时,可以在 shell 中做一些事情。

    您需要使用引号。所以而不是

    ./myprog      aaa      bbb
    

    你需要做其中之一

    ./myprog "     aaa      bbb"
    ./myprog '     aaa      bbb'
    

    这会将一个带有所有空格的参数传递给程序。两者之间有区别,第二个是字面量,就是它出现的字符串(除了'必须输入为\')。第一个将解释一些字符,但分成几个参数。有关更多信息,请参阅 shell 引用。所以不需要重写shell,shell设计者已经想到了。但是,由于它现在是一个参数,因此您将不得不在程序中进行更多的传递。

    选项 2

    通过标准输入传入数据。这是将大量数据放入命令的正常方式。例如

    ./myprog << EOF
        aaa      bbb
    EOF
    

    或者

    ./myprog
    Tell me what you want to tell me:
    aaaa bbb
    ctrl-d

    (斜体为程序输出)

    • 0

相关问题

  • 使 mysql CLI 以交互方式向我询问密码

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • grep -v grep 有什么作用

  • 没有服务器的命令行 pub/sub?

  • 如何将带有〜的路径保存到变量中?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve