AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 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

É possível que um programa obtenha o número de espaços entre os argumentos da linha de comando no POSIX?

  • 772

Digamos se eu escrevi um programa com a seguinte linha:

int main(int argc, char** argv)

Agora ele sabe quais argumentos de linha de comando são passados ​​para ele, verificando o conteúdo de argv.

O programa pode detectar quantos espaços entre os argumentos? Como quando eu digito isso no bash:

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

O ambiente é um Linux moderno (como o Ubuntu 16.04), mas suponho que a resposta deva se aplicar a qualquer sistema compatível com POSIX.

shell command-line
  • 6 6 respostas
  • 4703 Views

6 respostas

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

    Em geral, não. A análise da linha de comando é feita pelo shell, que não disponibiliza a linha não analisada para o programa chamado. Na verdade, seu programa pode ser executado a partir de outro programa que criou o argv não analisando uma string, mas construindo uma matriz de argumentos programaticamente.

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

    Não faz sentido falar de "espaços entre argumentos"; isso é um conceito de shell.

    O trabalho de um shell é pegar linhas inteiras de entrada e transformá-las em arrays de argumentos para iniciar os comandos. Isso pode envolver a análise de strings entre aspas, expansão de variáveis, curingas de arquivo e expressões de til e muito mais. O comando é iniciado com uma execchamada de sistema padrão, que aceita um vetor de strings.

    Existem outras maneiras de criar um vetor de strings. Muitos programas bifurcam e executam seus próprios subprocessos com invocações de comando predeterminadas - nesse caso, nunca existe uma "linha de comando". Da mesma forma, um shell gráfico (desktop) pode iniciar um processo quando um usuário arrasta um ícone de arquivo e o solta em um widget de comando - novamente, não há linha de texto para ter caracteres "entre" argumentos.

    No que diz respeito ao comando invocado, o que acontece em um shell ou outro processo pai/precursor é privado e oculto - vemos apenas a matriz de strings que o padrão C especifica que main()pode aceitar.

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

    Não, isso não é possível, a menos que os espaços façam parte de um argumento.

    O comando acessa os argumentos individuais de uma matriz (em uma forma ou outra, dependendo da linguagem de programação) e a linha de comando real pode ser salva em um arquivo de histórico (se digitado em um prompt interativo em um shell que possui arquivos de histórico), mas é nunca passou para o comando de nenhuma forma.

    Todos os comandos no Unix são no final executados por uma das exec()famílias de funções. Eles recebem o nome do comando e uma lista ou matriz de argumentos. Nenhum deles usa uma linha de comando digitada no prompt do shell. A system()função faz, mas seu argumento de string é executado posteriormente por execve(), que, novamente, recebe uma matriz de argumentos em vez de uma string de linha de comando.

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

    Em geral, não é possível, como várias outras respostas explicadas.

    No entanto, shells Unix são programas comuns (e eles estão interpretando a linha de comando e a englobando , ou seja, expandindo o comando antes de fazer fork& execvepara ele). Veja esta explicação sobre bashas operações do shell . Você pode escrever seu próprio shell (ou corrigir algum shell de software livre existente, por exemplo, GNU bash ) e usá-lo como seu shell (ou até mesmo seu shell de login, consulte passwd(5) & shells(5) ).

    Por exemplo, você pode ter seu próprio programa de shell colocando a linha de comando completa em alguma variável de ambiente (imagine, MY_COMMAND_LINEpor exemplo) - ou usar qualquer outro tipo de comunicação entre processos para transmitir a linha de comando do shell para o processo filho.

    Não entendo por que você gostaria de fazer isso, mas pode codificar um shell se comportando dessa maneira (mas recomendo não fazê-lo).

    BTW, um programa pode ser iniciado por algum programa que não seja um shell (mas que fork(2) depois execve(2) , ou apenas um execvepara iniciar um programa em seu processo atual). Nesse caso, não há linha de comando e seu programa pode ser iniciado sem um comando...

    Observe que você pode ter algum sistema Linux (especializado) sem nenhum shell instalado. Isso é estranho e incomum, mas possível. Você precisará escrever um programa init especializado iniciando outros programas conforme necessário - sem usar nenhum shell, mas fazendo fork& execvechamadas de sistema.

    Leia também Sistemas Operacionais : Três peças fáceis e não se esqueça que execveé praticamente sempre uma chamada do sistema (no Linux, elas estão listadas em syscalls(2) , veja também intro(2) ) que reinicializa o espaço de endereço virtual (e alguns outros coisas) do processo de fazê-lo.

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

    Você sempre pode dizer ao seu shell para informar aos aplicativos qual código do shell leva à sua execução. Por exemplo, com zsh, passando essa informação na $SHELL_CODEvariável de ambiente usando o preexec()gancho ( printenvusado como exemplo, você usaria getenv("SHELL_CODE")em seu programa):

    $ 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
    

    Todos aqueles seriam executados printenvcomo:

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

    Permitindo printenvrecuperar o código zsh que leva à execução de printenvcom esses argumentos. O que você gostaria de fazer com essa informação não está claro para mim.

    Combash , o recurso mais próximo de zsh's preexec()seria usá-lo $BASH_COMMANDem uma DEBUGarmadilha, mas observe que bashele faz algum nível de reescrita nisso (e, em particular, refatora parte do espaço em branco usado como delimitador) e isso é aplicado a todos (bem, alguns) comandos run, não toda a linha de comando inserida no prompt (consulte também a functraceopção).

    $ 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')
    

    Veja como alguns dos espaços que são delimitadores na sintaxe da linguagem shell foram espremidos em 1 e como nem sempre a linha de comando completa é passada para o comando. Portanto, provavelmente não é útil no seu caso.

    Observe que eu não aconselharia fazer esse tipo de coisa, pois você está potencialmente vazando informações confidenciais para todos os comandos, como em:

    echo very_secret | wc -c | untrustedcmd
    

    vazaria esse segredo para ambos wce untrustedcmd.

    Claro, você poderia fazer esse tipo de coisa para outras linguagens além do shell. Por exemplo, em C, você poderia usar algumas macros que exportam o código C que executa um comando para o ambiente:

    #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;
    }
    

    Exemplo:

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

    Veja como alguns espaços foram condensados ​​pelo pré-processador C como no caso do bash. Na maioria, se não em todas as linguagens, a quantidade de espaço usada nos delimitadores não faz diferença, então não é surpresa que o compilador/intérprete tome alguma liberdade com eles aqui.

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

    Vou apenas adicionar o que está faltando nas outras respostas.

    Não

    Ver outras respostas

    Talvez, tipo

    Não há nada que possa ser feito no programa, mas há algo que pode ser feito no shell quando você executa o programa.

    Você precisa usar aspas. Então, em vez de

    ./myprog      aaa      bbb
    

    você precisa fazer um desses

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

    Isso passará um único argumento para o programa, com todos os espaços. Existe uma diferença entre os dois, o segundo é literal, exatamente a string como aparece (só que 'deve ser digitado como\' ). A primeira vai interpretar alguns caracteres, mas dividida em vários argumentos. Consulte as citações do shell para obter mais informações. Portanto, não há necessidade de reescrever o shell, os projetistas do shell já pensaram nisso. No entanto, como agora é um argumento, você terá que passar mais no programa.

    opção 2

    Passe os dados via stdin. Esta é a maneira normal de obter grandes quantidades de dados em um comando. por exemplo

    ./myprog << EOF
        aaa      bbb
    EOF
    

    ou

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

    (O itálico é a saída do programa)

    • 0

relate perguntas

  • Fazendo mysql CLI me pedir uma senha interativamente

  • Existe uma maneira de fazer ls mostrar arquivos ocultos apenas para determinados diretórios?

  • o que grep -v grep faz

  • Pub / sub de linha de comando sem um servidor?

  • Como salvar um caminho com ~ em uma variável?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    ssh Não é possível negociar: "nenhuma cifra correspondente encontrada", está rejeitando o cbc

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    Como descarregar o módulo do kernel 'nvidia-drm'?

    • 13 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add retorna com: "Erro ao conectar ao agente: nenhum arquivo ou diretório" 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +0800 CST

Hot tag

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

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve