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 / 510220
Accepted
user149572
user149572
Asked: 2019-04-03 22:49:19 +0800 CST2019-04-03 22:49:19 +0800 CST 2019-04-03 22:49:19 +0800 CST

O que é "declarar" no Bash?

  • 772

Depois de ler a resposta de ilkkachu a esta pergunta , aprendi sobre a existência do shell declare(com argumento -n) embutido.

help declaretraz:

Defina valores e atributos de variáveis.

Declare variáveis ​​e dê-lhes atributos. Se nenhum NOME for fornecido, exiba os atributos e valores de todas as variáveis.

-n ... torna NAME uma referência à variável nomeada por seu valor

Peço uma explicação geral com um exemplo a respeito declareporque não entendo o man. Eu sei o que é uma variável e expandi-la, mas ainda sinto falta do man( declareatributo variável?).

Talvez você queira explicar isso com base no código de ilkkachu na resposta:

#!/bin/bash
function read_and_verify  {
    read -p "Please enter value for '$1': " tmp1
    read -p "Please repeat the value to verify: " tmp2
    if [ "$tmp1" != "$tmp2" ]; then
        echo "Values unmatched. Please try again."; return 2
    else
        declare -n ref="$1"
        ref=$tmp1
    fi
}
bash function
  • 4 4 respostas
  • 24588 Views

4 respostas

  • Voted
  1. Best Answer
    sudodus
    2020-02-06T07:04:02+08:002020-02-06T07:04:02+08:00

    In most cases it is enough with an implicit declaration in bash

    asdf="some text"
    

    But, sometimes you want a variable's value to only be integer (so in case it would later change, even automatically, it could only be changed to an integer, defaults to zero in some cases), and can use:

    declare -i num
    

    or

    declare -i num=15
    

    Sometimes you want arrays, and then you need declare

    declare -a asdf   # indexed type
    

    or

    declare -A asdf   # associative type
    

    You can find good tutorials about arrays in bash when you browse the internet with the search string 'bash array tutorial' (without quotes), for example

    linuxconfig.org/how-to-use-arrays-in-bash-script


    I think these are the most common cases when you declare variables.


    Please notice also, that

    • in a function, declare makes the variable local (in the function)
    • without any name, it lists all variables (in the active shell)

      declare
      

    Finally, you get a brief summary of the features of the shell built-in command declare in bash with the command

    help declare
    
    • 28
  2. fra-san
    2019-04-04T10:47:07+08:002019-04-04T10:47:07+08:00

    A saída de help declareé bastante concisa. Uma explicação mais clara pode ser encontrada em man bashou info bash— sendo este último a fonte do que se segue.

    Primeiro, algumas definições. Sobre variáveis ​​e atributos :

    Um parâmetro é uma entidade que armazena valores. ... Uma variável é um parâmetro denotado por um name. Uma variável tem um valor e zero ou mais atributos . Os atributos são atribuídos usando o declarecomando interno ...

    E sobre o declare embutido :

    declare

    declare [-aAfFgilnrtux] [-p] [name[=value] …]
    

    Declare variáveis ​​e dê-lhes atributos. Se nenhum nome for fornecido, exiba os valores das variáveis.

    ...

    -n
    Dê a cada nome o atributo nameref , tornando-o uma referência de nome para outra variável. Essa outra variável é definida pelo valor de name . Todas as referências, atribuições e modificações de atributo para name , exceto aquelas que usam ou alteram o -npróprio atributo, são realizadas na variável referenciada pelo valor de name . ...

    Observe que as variáveis ​​de referência de nome estão disponíveis apenas no Bash 4.3 ou posterior 1 .

    Além disso, para uma introdução útil declaree atributos de variáveis ​​no Bash, eu indicaria esta resposta para "O que fazer declare namee declare -gfazer?" (que se concentra principalmente no escopo das variáveis).


    Basicamente 2 , declare name=[value]é equivalente à atribuição com a name=[value]qual você provavelmente está familiarizado. Em ambos os casos, nameé atribuído o valor nulo se valueestiver ausente.

    Observe que o ligeiramente diferente declare name, em vez disso, não define a variável name3 :

    $ declare name
    
    ## With the -p option, declare is used to display
    ## attributes and values of variables
    $ declare -p name
    declare -- name            ## "name" exists
    
    ## Parameter expansion can be used to reveal if a variable is set:
    ## "isunset" is substituted to "name" only if unset 
    $ echo "${name-isunset}"
    isunset
    

    Assim, a variável namepode ser:

    • declarado e desarmado , depois declare name;
    • declarado e definido com null como valor, após name=ou declare name=;
    • declarado , definido e com um valor não nulo após name=valueou declare name=value.

    De forma geral,declare [options] name=value

    1. cria a variável name— que é um parâmetro com um nome, que por sua vez é apenas uma parte da memória que você pode usar para armazenar informações 4 ;
    2. atribui o valor valuea ele;
    3. opcionalmente define nameos atributos de , que definem tanto o tipo de valor que ele pode armazenar (não em termos de um tipo , estritamente falando, já que a linguagem do Bash não é tipada) quanto as maneiras pelas quais ele pode ser manipulado.

    Atributos são provavelmente mais fáceis de explicar com um exemplo: using declare -i nameirá definir o atributo "integer" de name, deixando-o ser tratado como um inteiro; citando o manual , "a avaliação aritmética será realizada quando a variável receber um valor":

    ## Let's compare an ordinary variable with an integer
    $ declare var
    $ declare -i int
    $ var="1+1"
    $ int="1+1"
    $ echo "$var"
    1+1                 ## The literal "1+1"
    $ echo "$int"
    2                   ## The result of the evaluation of 1+1
    

    À luz do exposto, o que está acontecendo no código de ilkkachu é que:

    1. Uma variável nomeada refé declarada, com o atributo "nameref" definido, e o conteúdo de $1(o primeiro argumento posicional) é atribuído a ela:

      declare -n ref="$1"
      

      O objetivo de uma variável de referência de nome, como refé manter o nome de outra variável, que geralmente não seria conhecida antecipadamente, possivelmente porque queremos que ela seja definida dinamicamente (por exemplo, porque queremos reutilizar um pedaço de código e tê-lo aplicado a várias variáveis) e para fornecer uma maneira conveniente de se referir a ela (e manipulá-la). (Mas não é o único: a indireção é uma alternativa; veja Expansão de Parâmetros do Shell ).

    2. Quando o valor da variável tmp1é atribuído a ref:

      ref=$tmp1
      

      uma variável adicional, cujo nome é o valor de ref, é declarada implicitamente. O valor de tmp1também é atribuído indiretamente à variável declarada implicitamente por meio dessa atribuição explícita a ref.

    No contexto da sua pergunta vinculada , chamando read_and_verifycomo

    read_and_verify domain "Prompt text here..."
    

    declarará a variável domaine atribuirá a ela o valor de tmp1(ou seja, a entrada do usuário). Ele é exatamente projetado para reutilizar o código que interage com o usuário e alavancar uma variável nameref para declarar domaine algumas outras variáveis.

    Para dar uma olhada mais de perto na parte implícita, podemos reproduzir o processo passo a passo:

    ## Assign a value to the first positional argument
    $ set -- "domain"
    
    ## Declare the same "tmp1" variable as in your code
    $ tmp1="value for domain"
    
    ## Declare a "ref" variable with the nameref attribute set and
    ## assign the value "domain" to it
    $ declare -n ref="$1"
    
    ## Note that there is no "domain" variable yet
    $ declare -p domain
    bash: declare: domain: not found
    
    ## Assign a value to "ref" and, indirectly, to the "domain" variable
    ## that is implicitly declared  
    $ ref=$tmp1
    
    ## Verify that a variable named "domain" now exists, and that
    ## its value is that of "tmp1"
    $ declare -p domain
    declare -- domain="value for domain"
    
    ## Verify that "ref" is actually a reference to "domain"
    $ domain="new value"
    $ echo "$domain"
    new value
    $ declare -p ref
    declare -n ref="domain"
    $ echo "$ref"
    new value
    

    1 Referência: arquivo CHANGES , seção "3. Novos recursos no Bash", ponto "w".
    Isso pode ser relevante: por exemplo, o CentOS Linux 7.6 (atualmente a versão mais recente) é fornecido com o Bash 4.2 .

    2 Como de costume com shell builtins, uma explicação exaustiva e concisa é indescritível, pois eles executam várias ações possivelmente heterogêneas. Vou me concentrar apenas em declarar, atribuir e definir atributos e considerarei listar, definir o escopo e remover atributos como fora do escopo desta resposta.

    3 Este comportamento declare -pfoi introduzido no Bash 4.4. Referência: arquivo CHANGES , seção "3. Novos recursos no Bash", ponto "f".
    Como G-Man apontou nos comentários, no Bash 4.3 declare name; declare -p namegera um erro. Mas você ainda pode verificar se nameexiste com declare -p | grep 'declare -- name'.

    4 FullBashGuide, Parâmetros em mywiki.wooledge.org

    • 20
  3. LL3
    2019-04-06T10:03:54+08:002019-04-06T10:03:54+08:00

    Vou tentar explicar isso, mas me perdoe se não seguir o exemplo que você forneceu. Prefiro tentar guiá-lo ao longo de minha própria abordagem diferente.

    Você diz que já entende conceitos como “variáveis” e “expandindo-as”, etc., então vou apenas passar por cima de alguns conhecimentos básicos que, de outra forma, exigiriam um foco mais profundo.

    Então, vou começar dizendo que, em seu nível mais básico , o declarecomando é apenas uma maneira de você dizer ao Bash que você precisa de um valor de variável (ou seja, um valor que pode mudar durante a execução do script), e que você irá se referir a esse valor usando um nome específico, precisamente o nome que você indica ao lado do declarepróprio comando.

    Aquilo é:

    declare foo="bar"
    

    diz ao Bash que você quer que a variável nomeada footenha o valor bar.

    Mas .. espere um minuto .. podemos fazer isso sem usar declarenada, não podemos. Como em:

    foo="bar"
    

    Muito verdadeiro.

    Bem, acontece que a atribuição simples acima é na verdade uma maneira implícita para... na verdade... declarar uma variável.

    ( Acontece também que o acima é uma das poucas maneiras de alterar o valor da variável nomeada foo; na verdade, é precisamente a maneira mais direta, concisa, evidente, direta.. mas não é a única.. . . Voltarei a isso mais tarde .. ).

    Mas então, se é tão bem possível declarar um “nome que marcará valores de variáveis” (apenas “variável” daqui em diante, por uma questão de brevidade) sem usar declare nada, por que você iria querer usar este pomposo “declarar “comando?

    A resposta está no fato de que a maneira implícita acima de declarar uma variável ( foo="bar"), ela... implicitamente... faz com que o Bash considere essa variável como sendo do tipo que é mais comumente usado no cenário de uso típico de um shell.

    Esse tipo é o tipo string, ou seja, uma sequência de caracteres sem significado específico. Portanto, uma string é o que você obtém quando usa a declaração implícita.

    Mas você, como programador, às vezes precisa considerar uma variável como, por exemplo, um número .. no qual você precisa fazer operações aritméticas .. e usar uma declaração implícita como foo=5+6 não fará com que o Bash atribua o valor 11 foocomo você faria Espero. Em vez disso, atribuirá à foosequência dos três caracteres 5 + 6.

    Então .. você precisa de uma maneira de dizer ao Bash que você deseja foo ser considerado um número, não uma string .. e é para isso que um explícito declare é útil.

    Apenas diga:

    declare -i foo=5+6  # <<- note the '-i' option: it means 'integer'
    

    e o Bash felizmente fará as contas para você e atribuirá o valor numérico 11 à variável foo.

    Ou seja: ao dizer declare -i foovocê dá à variável fooo atributo de ser um número inteiro.

    Declarar números (precisamente inteiros, porque o Bash ainda não entende decimais, pontos flutuantes e tudo isso) pode ser a primeira razão para usar declare, mas não é a única razão. Como você já entendeu, existem alguns outros atributos que você pode dar às variáveis. Por exemplo, você pode ter Bash para sempre tornar o valor de uma variável em maiúscula, não importa o quê: se você disser declare -u foo, a partir de então, quando você disser foo=barBash, na verdade, atribui a string BARà variável foo.

    Para dar qualquer um desses atributos a uma variável, você deve usar o declarecomando, não há outra escolha.


    Agora, um outro dos atributos que você pode fornecer declareé o infame “name-ref”, o -natributo. ( E agora vou retomar o conceito que coloquei em espera anteriormente ).

    O atributo name-ref, basicamente, permite aos programadores Bash outra maneira de alterar o valor de uma variável. Mais precisamente, fornece uma maneira indireta de fazer isso.

    Veja como funciona:

    Você é declareuma variável que tem o -natributo, e é muito recomendado (embora não seja estritamente obrigatório, mas torna as coisas mais simples) que você também dê um valor para essa mesma variável no mesmo declarecomando. Assim:

    declare -n baz="foo"
    

    Isso diz ao Bash que, a partir de então, cada vez que você usar ou alterar o valor da variável chamada baz, ele deverá realmente usar ou alterar o valor da variável chamada foo.

    O que significa que, a partir de então, você pode dizer algo como baz=10+3fazer fooobter o valor de 13. Assumindo, é claro, que foofoi declarado anteriormente como inteiro ( declare -i) como fizemos apenas um minuto atrás, caso contrário, obterá a sequência dos quatro personagens 1 0 + 3.

    Além disso: se você alterar fooo valor de 's diretamente, como em foo=15, você verá 15 também dizendo echo “${baz}”. Isso ocorre porque a variável bazdeclarada como name-ref de foosempre reflete fooo valor de .

    O declare -ncomando acima é chamado de “nome-referência” porque faz a variável baz se referir ao nome de outra variável. De fato, declaramos baz ter o valor "foo" que, por causa da -nopção, é tratado pelo Bash como o nome de outra variável.

    Agora, por que diabos você iria querer fazer isso?

    Bem.. vale dizer que este é um recurso para necessidades bastante avançadas.

    Na verdade, tão avançado que, quando um programador enfrenta um problema que realmente exigiria uma referência de nome, também é provável que esse problema deva ser resolvido usando uma linguagem de programação adequada em vez de Bash.

    Uma dessas necessidades avançadas é, por exemplo, quando você, como programador, não consegue saber durante o desenvolvimento qual variável terá que usar em um ponto específico de um script, mas será totalmente conhecido dinamicamente em tempo de execução. E dado que não há como nenhum programador intervir em tempo de execução, a única opção é fazer uma provisão prévia para tal situação no script, e um “name-ref” pode ser a única maneira viável. Como um caso de uso amplamente conhecido dessa necessidade avançada, pense nos plug-ins, por exemplo. O programador de um programa “pluginable” precisa fazer provisão genérica para plug-ins futuros (e possivelmente de terceiros) de antemão. Portanto, o programador precisará usar recursos como um name-ref no Bash.

    Uma outra necessidade avançada é quando você tem que lidar com uma grande quantidade de dados na RAM e também precisa passar esses dados para funções do seu script que também precisam modificar esses dados ao longo do caminho. Nesse caso, você certamente poderia copiar esses dados de uma função para outra (como Bash faz quando você faz dest_var="${src_var}"ou quando invoca funções como em myfunc "${src_var}"), mas sendo esses dados uma grande quantidade, isso geraria um enorme desperdício de RAM e muito operação ineficiente. Portanto, a solução se tais situações surgirem é usar não uma cópia dos dados, mas uma referênciaa esses dados. Em Bash, um nome-ref. Este caso de uso é realmente a norma em qualquer linguagem de programação moderna, mas é bastante excepcional quando se trata do Bash, porque o Bash é projetado principalmente para scripts simples e curtos que lidam principalmente com arquivos e comandos externos e, portanto, os scripts Bash raramente precisam passar grandes quantidade de dados entre as funções. E quando as funções de um script precisam compartilhar alguns dados (acessá-los e modificá-los), isso geralmente é obtido usando apenas uma variável global, o que é bastante normal em scripts Bash, tanto quanto é muito obsoleto em linguagens de programação adequadas.

    Então, pode haver um caso de uso notável para name-refs no Bash e (talvez ironicamente) está associado a quando você usa ainda outros tipos de variáveis:

    1. variáveis ​​que são declaradas como “arrays indexados” ( declare -a)
    2. variables that are declared as “associative arrays” (declare -A).

    These are a type of variables that may be more easily (as well as more efficiently) passed along functions by using name-refs instead of by normal copying, even when they don’t carry huge amounts of data.

    If all these examples sound weird, and still incomprehensible, it’s only because name-refs are indeed an advanced topic, and a rare need for the typical usage scenario of Bash.

    I could tell you about occasions on which I for one have found use for name-refs in Bash, but so far they have been mostly for quite “esoteric” and complicated needs, and I’m afraid that if I described them I would only complicate things for you at this point of your learning. Just to mention the least complex (and possibly not esoteric): returning values from functions. Bash does not really support this functionality, so I obtained the same by using name-refs. This, incidentally, is exactly what your example code does.


    Besides this, a small personal advice, which would actually be better suited for a comment but I haven't been able to condense it enough to fit into StackExchange's comment's limits.

    I think that the most you should do at the moment is to just experiment with name-refs by using the simple examples I showed and maybe with the example code you provided, disregarding for the moment the “why on earth” part and focusing only on the “how it works” part. By experimenting a bit, the “how” part may sink better into your mind, so that the “why” part will come clear to you in due time when (or if) you’ll have a real practical problem for which a name-ref would truly come in handy.

    • 14
  4. Kusalananda
    2019-04-06T00:21:58+08:002019-04-06T00:21:58+08:00

    Em geral, declareno bashshell define (ou remove ou exibe) atributos em variáveis. Um atributo é um tipo de anotação que diz "esta é uma referência de nome", ou "esta é uma matriz associativa", ou "esta variável deve sempre ser avaliada como um inteiro" ou "esta variável é somente leitura e não pode ser redefinido", ou "esta variável é exportada (uma variável de ambiente)" etc.

    O typesetbuilt-in é sinônimo de declarein bash, como typeseté usado em outros shells ( ksh, onde se originou e zsh, por exemplo) para definir atributos de variáveis.


    Olhando mais de perto o exemplo de referência de nome na pergunta:

    A função shell que você mostra, com um pouco de código adicionado que a usa:

    #!/bin/bash
    
    function read_and_verify  {
        read -p "Please enter value for '$1': " tmp1
        read -p "Please repeat the value to verify: " tmp2
        if [ "$tmp1" != "$tmp2" ]; then
            echo "Values unmatched. Please try again."; return 2
        else
            declare -n ref="$1"
            ref=$tmp1
        fi
    }
    
    read_and_verify foo
    printf 'The variable "foo" has the value "%s"\n' "$foo"
    

    Executando isso:

    $ bash script.sh 
    Por favor, insira o valor para 'foo': hello 
    Por favor, repita o valor para verificar: hello?
    Valores incomparáveis. Por favor, tente novamente.
    A variável "foo" tem o valor ""
    

    Isso mostra que a foovariável não está sendo definida como nada quando o usuário insere duas strings diferentes .

    $ bash script.sh 
    Por favor insira o valor para 'foo': olá 
    Por favor, repita o valor para verificar: olá
    A variável "foo" tem o valor "hello"
    

    Isso mostra que a variável fooé definida para a string que o usuário digitou quando digitou a mesma string duas vezes.

    A maneira que $fooobtém o valor hellona parte principal do script é pelas seguintes linhas na função shell:

    declare -n ref="$1"
    ref=$tmp1
    

    onde $tmp1é a string helloinserida pelo usuário e $1é a string foopassada na linha de comando da função da parte principal do script.

    Observe que a refvariável é declarada declare -ncomo uma variável de referência de nome e que o valor fooé fornecido como o valor nessa declaração. Isso significa que a partir desse ponto, até que a variável saia do escopo, qualquer uso da variável refserá o mesmo que usar foo. A variável refé uma variável de referência de nome referenciadafoo neste ponto.

    Isso tem como consequência que atribuir um valor a ref, como é feito na linha após a declaração, atribuirá o valor a foo.

    O valor helloestá disponível na $fooparte principal do script.

    • 6

relate perguntas

  • exportar variáveis ​​​​env programaticamente, via stdout do comando [duplicado]

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • ordem de substituição de processos `te` e `bash`

  • Execute um script muito lento até que seja bem-sucedido

Sidebar

Stats

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

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

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

    • 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

    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
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • 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
    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

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