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 / computer / Perguntas / 1839405
Accepted
pmor
pmor
Asked: 2024-04-17 21:30:39 +0800 CST2024-04-17 21:30:39 +0800 CST 2024-04-17 21:30:39 +0800 CST

Como executar o comando contendo a variável string da variável string?

  • 772

Estou tentando executar o comando que contém a variável string da variável string:

$ X="bash -c 'echo OK'" ; $X
OK': -c: line 1: unexpected EOF while looking for matching `''

Por que não funciona? Como consertar/fazer corretamente?

Observe que sou obrigado a executar o comando como $X.

Observe que sou obrigado a executar echo OKvia bash -c.

bash
  • 2 2 respostas
  • 34 Views

2 respostas

  • Voted
  1. Best Answer
    fraxflax
    2024-04-17T21:52:31+08:002024-04-17T21:52:31+08:00

    porque ele tenta executar o comando chamadobash -c 'echo OK'

    eval $Xpodia funcionar

    • 2
  2. harrymc
    2024-04-17T23:41:37+08:002024-04-17T23:41:37+08:00

    O post Como podemos executar um comando armazenado em uma variável? contém esta longa resposta de ilkkachu que discute a questão de uma maneira muito completa.

    Vou citar esta excelente resposta aqui.

    Isso foi discutido em uma série de questões no unix.SE. Tentarei coletar todos os problemas que puder surgir aqui. Abaixo está

    • uma descrição de por que e como as várias tentativas falham,
    • uma maneira de fazer isso corretamente com uma função (para um comando fixo), ou
    • com arrays shell (Bash/ksh/zsh) ou o $@pseudo-array (POSIX sh), ambos os quais também permitem construir as peças da linha de comando, se você, por exemplo, precisar apenas variar algumas opções
    • e notas sobre como usar evalpara fazer isso.

    Algumas referências no final.

    Para os propósitos aqui, não importa muito se são apenas os argumentos do comando ou também o nome do comando que deve ser armazenado em uma variável. Eles são processados ​​de forma semelhante até o ponto em que o comando é iniciado, momento em que o shell apenas considera a primeira palavra como o nome do comando a ser executado.


    Por que falha

    A razão pela qual você enfrenta esses problemas é o fato de que a divisão de palavras é bastante simples e não se presta a casos complexos, e o fato de que aspas expandidas de variáveis ​​não agem como aspas, mas são apenas caracteres comuns.

    (Observe que a parte sobre aspas é semelhante a qualquer outra linguagem de programação: por exemplo, char *s = "foo()"; printf("%s\n", s)não chama a função foo()em C, mas apenas imprime a string foo(). Isso é diferente em processadores de macro, como m4, o pré-processador C ou Make (até certo ponto) . O shell é uma linguagem de programação, não um processador de macro.)

    Em sistemas do tipo Unix, é o shell que processa aspas e expansões de variáveis ​​na linha de comando, transformando-as de uma única string na lista de strings que a chamada do sistema subjacente passa para o comando iniciado. O próprio programa não vê as cotações processadas pelo shell. Por exemplo, se for dado o comando ls -l "foo bar", o shell transforma isso em três strings ls, -land foo bar (removendo as aspas) e as passa para ls. (Até o nome do comando é passado, embora nem todos os programas o utilizem.)

    Os casos apresentados na pergunta:

    A atribuição aqui atribui a string única ls -l "/tmp/test/my dir"a abc:

    $ abc='ls -l "/tmp/test/my dir"'
    

    Abaixo, é $abcdividido em espaços em branco e lsobtém os três argumentos -le "/tmp/test/my. dir"As aspas aqui são apenas dados, então há uma na frente do segundo argumento e outra atrás do terceiro. A opção funciona, mas o caminho é processado incorretamente, pois lsvê as aspas como parte dos nomes dos arquivos:

    $ $abc
    ls: cannot access '"/tmp/test/my': No such file or directory
    ls: cannot access 'dir"': No such file or directory
    

    Aqui, a expansão é citada, por isso é mantida como uma única palavra. O shell tenta encontrar um programa chamado literalmente ls -l "/tmp/test/my dir", espaços e aspas incluídos.

    $ "$abc"
    bash: ls -l "/tmp/test/my dir": No such file or directory
    

    E aqui, $abcé dividido, e apenas a primeira palavra resultante é considerada como argumento para -c, então o Bash apenas é executado lsno diretório atual. As outras palavras são argumentos para bash e são usadas para preencher $0, $1, etc.

    $ bash -c $abc
    'my dir'
    

    Com bash -c "$abc", e eval "$abc", há uma etapa adicional de processamento do shell, que faz com que as aspas funcionem, mas também faz com que todas as expansões do shell sejam processadas novamente , portanto, há o risco de execução acidental, por exemplo, uma substituição de comando dos dados fornecidos pelo usuário, a menos que você esteja muito cuidado ao citar.


    Melhores maneiras de fazer isso

    As duas melhores maneiras de armazenar um comando são a) usar uma função, b) usar uma variável de array (ou os parâmetros posicionais).

    Usando funções:

    Simplesmente declare uma função com o comando dentro e execute a função como se fosse um comando. As expansões nos comandos dentro da função são processadas apenas quando o comando é executado, não quando é definido, e você não precisa citar os comandos individuais. Embora isso realmente só ajude se você tiver um comando fixo que precisa armazenar (ou mais de um comando fixo).

    # define it
    myls() {
        ls -l "/tmp/test/my dir"
    }
    
    # run it
    myls
    

    Também é possível definir múltiplas funções e usar uma variável para armazenar o nome da função que você deseja executar no final.

    Usando uma matriz:

    Matrizes permitem criar variáveis ​​com múltiplas palavras onde as palavras individuais contêm espaços em branco. Aqui, as palavras individuais são armazenadas como elementos distintos do array, e a "${array[@]}"expansão expande cada elemento como palavras de shell separadas:

    # define the array
    mycmd=(ls -l "/tmp/test/my dir")
    
    # expand the array, run the command
    "${mycmd[@]}"
    

    O comando é escrito entre parênteses exatamente como seria escrito ao executar o comando. O processamento que o shell faz é o mesmo em ambos os casos, apenas em um ele salva apenas a lista de strings resultante, em vez de usá-la para executar um programa.

    A sintaxe para expandir o array posteriormente é um pouco horrível, e as aspas em torno dele são importantes.

    Os arrays também permitem construir a linha de comando peça por peça. Por exemplo:

    mycmd=(ls)               # initial command
    if [ "$want_detail" = 1 ]; then
        mycmd+=(-l)          # optional flag, append to array
    fi
    mycmd+=("$targetdir")    # the filename
    
    "${mycmd[@]}"
    

    ou mantenha partes da linha de comando constantes e use o array para preencher apenas uma parte dela, como opções ou nomes de arquivos:

    options=(-x -v)
    files=(file1 "file name with whitespace")
    target=/somedir
    
    somecommand "${options[@]}" "${files[@]}" "$target"
    

    ( somecommandsendo um nome de espaço reservado genérico aqui, não um comando real.)

    A desvantagem dos arrays é que eles não são um recurso padrão, então shells POSIX simples (como dasho padrão /bin/shno Debian/Ubuntu) não os suportam (mas veja abaixo). Bash, ksh e zsh, entretanto, é provável que seu sistema tenha algum shell que suporte arrays.

    Usando"$@"

    Em shells sem suporte para arrays nomeados, ainda é possível usar os parâmetros posicionais (o pseudo-array "$@") para armazenar os argumentos de um comando.

    A seguir devem ser bits de script portáteis que fazem o equivalente aos bits de código da seção anterior. A matriz é substituída por "$@", a lista de parâmetros posicionais. A configuração "$@"é feita com set, e as aspas duplas "$@"são importantes (elas fazem com que os elementos da lista sejam citados individualmente).

    Primeiro, basta armazenar um comando com argumentos "$@"e executá-lo:

    set -- ls -l "/tmp/test/my dir"
    "$@"
    

    Configurando condicionalmente partes das opções de linha de comando para um comando:

    set -- ls
    if [ "$want_detail" = 1 ]; then
        set -- "$@" -l
    fi
    set -- "$@" "$targetdir"
    
    "$@"
    

    Usando apenas "$@"para opções e operandos:

    set -- -x -v
    set -- "$@" file1 "file name with whitespace"
    set -- "$@" /somedir
    
    somecommand "$@"
    

    Claro, "$@"geralmente é preenchido com os argumentos do próprio script, então você terá que salvá-los em algum lugar antes de redefini-los "$@".

    Para passar condicionalmente um único argumento, você também pode usar a expansão de valor alternativo ${var:+word}com algumas citações cuidadosas. Aqui, incluímos -f e o nome do arquivo somente se o nome do arquivo não estiver vazio:

    file="foo bar"
    somecommand ${file:+-f "$file"}
    

    Usando eval(tenha cuidado aqui!)

    evalpega uma string e a executa como um comando, como se tivesse sido inserida na linha de comando do shell. Isso inclui todo o processamento de cotações e expansões, que é útil e perigoso.

    No caso simples, permite fazer exatamente o que queremos:

    cmd='ls -l "/tmp/test/my dir"'
    eval "$cmd"
    

    Com eval, as aspas são processadas, então lseventualmente vemos apenas os dois argumentos -le /tmp/test/my dir, como queremos. evaltambém é inteligente o suficiente para concatenar quaisquer argumentos obtidos, portanto eval $cmd também pode funcionar em alguns casos, mas, por exemplo, todas as execuções de espaços em branco seriam alteradas para espaços únicos. Ainda é melhor citar a variável ali, pois isso garantirá que ela não seja modificada para eval.

    No entanto, é perigoso incluir a entrada do usuário na sequência de comandos paraeval . Por exemplo, isso parece funcionar:

    read -r filename
    cmd="ls -ld '$filename'"
    eval "$cmd";
    

    Mas se o usuário fornecer uma entrada que contenha aspas simples, ele poderá sair das aspas e executar comandos arbitrários! Por exemplo, com o input '$(whatever)'.txt, seu script executa alegremente a substituição do comando. Que poderia ter sido rm -rf(ou pior).

    O problema é que o valor de $filenamefoi incorporado na linha de comando evalexecutada. Foi expandido antes eval, que viu, por exemplo, o comando ls -l ''$(whatever)'.txt'. Você precisaria pré-processar a entrada para estar seguro.

    Se fizermos ao contrário, mantendo o nome do arquivo na variável e deixando o evalcomando expandi-lo, fica mais seguro novamente:

    read -r filename
    cmd='ls -ld "$filename"'
    eval "$cmd";
    

    Observe que as aspas externas agora são aspas simples, portanto, expansões internas não acontecem. Conseqüentemente, evalvê o comando ls -l "$filename"e expande o próprio nome do arquivo com segurança.

    Mas isso não é muito diferente de apenas armazenar o comando em uma função ou array. Com funções ou arrays, esse problema não existe, pois as palavras são mantidas separadas o tempo todo e não há cotação ou outro processamento para o conteúdo de filename.

    read -r filename
    cmd=(ls -ld -- "$filename")
    "${cmd[@]}"
    

    Praticamente a única razão para usar evalé aquela em que a parte variável envolve elementos de sintaxe do shell que não podem ser trazidos por meio de variáveis ​​(pipelines, redirecionamentos, etc.). No entanto, você precisará citar/escapar de todo o resto na linha de comando que precisa de proteção contra a etapa de análise adicional (veja o link abaixo). De qualquer forma, é melhor evitar incorporar informações do usuário no evalcomando!


    Referências

    • Divisão de palavras no BashGuide
    • BashFAQ/050 ou "Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham!"
    • A pergunta Por que meu script de shell engasga com espaços em branco ou outros caracteres especiais? , que discute uma série de questões relacionadas a cotações e espaços em branco, incluindo o armazenamento de comandos.
    • Escape de uma variável para uso como conteúdo de outro script
    • https://unix.stackexchange.com/q/562870/170373
    • 1

relate perguntas

  • substituindo zsh por bash no usuário não root

  • Tendo problemas para definir variáveis ​​de ambiente no Terminal no macOS High Sierra

  • Existe um equivalente a cd - para cp ou mv?

  • Notificar-enviar notificações aparecendo na janela

  • como abrir um arquivo de escritório do WSL

Sidebar

Stats

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

    Como posso reduzir o consumo do processo `vmmem`?

    • 11 respostas
  • Marko Smith

    Baixar vídeo do Microsoft Stream

    • 4 respostas
  • Marko Smith

    O Google Chrome DevTools falhou ao analisar o SourceMap: chrome-extension

    • 6 respostas
  • Marko Smith

    O visualizador de fotos do Windows não pode ser executado porque não há memória suficiente?

    • 5 respostas
  • Marko Smith

    Como faço para ativar o WindowsXP agora que o suporte acabou?

    • 6 respostas
  • Marko Smith

    Área de trabalho remota congelando intermitentemente

    • 7 respostas
  • Marko Smith

    O que significa ter uma máscara de sub-rede /32?

    • 6 respostas
  • Marko Smith

    Ponteiro do mouse movendo-se nas teclas de seta pressionadas no Windows?

    • 1 respostas
  • Marko Smith

    O VirtualBox falha ao iniciar com VERR_NEM_VM_CREATE_FAILED

    • 8 respostas
  • Marko Smith

    Os aplicativos não aparecem nas configurações de privacidade da câmera e do microfone no MacBook

    • 5 respostas
  • Martin Hope
    Vickel O Firefox não permite mais colar no WhatsApp web? 2023-08-18 05:04:35 +0800 CST
  • Martin Hope
    Saaru Lindestøkke Por que os arquivos tar.xz são 15x menores ao usar a biblioteca tar do Python em comparação com o tar do macOS? 2021-03-14 09:37:48 +0800 CST
  • Martin Hope
    CiaranWelsh Como posso reduzir o consumo do processo `vmmem`? 2020-06-10 02:06:58 +0800 CST
  • Martin Hope
    Jim Pesquisa do Windows 10 não está carregando, mostrando janela em branco 2020-02-06 03:28:26 +0800 CST
  • Martin Hope
    andre_ss6 Área de trabalho remota congelando intermitentemente 2019-09-11 12:56:40 +0800 CST
  • Martin Hope
    Riley Carney Por que colocar um ponto após o URL remove as informações de login? 2019-08-06 10:59:24 +0800 CST
  • Martin Hope
    zdimension Ponteiro do mouse movendo-se nas teclas de seta pressionadas no Windows? 2019-08-04 06:39:57 +0800 CST
  • Martin Hope
    jonsca Todos os meus complementos do Firefox foram desativados repentinamente, como posso reativá-los? 2019-05-04 17:58:52 +0800 CST
  • Martin Hope
    MCK É possível criar um código QR usando texto? 2019-04-02 06:32:14 +0800 CST
  • Martin Hope
    SoniEx2 Altere o nome da ramificação padrão do git init 2019-04-01 06:16:56 +0800 CST

Hot tag

windows-10 linux windows microsoft-excel networking ubuntu worksheet-function bash command-line hard-drive

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