Quando precisamos de chaves em torno de variáveis ​​de shell?


659

Em scripts de shell, quando usamos {}ao expandir variáveis?

Por exemplo, eu vi o seguinte:

var=10        # Declare variable

echo "${var}" # One use of the variable
echo "$var"   # Another use of the variable

Existe uma diferença significativa ou é apenas estilo? Um prefere o outro?

Respostas:


753

Neste exemplo em particular, isso não faz diferença. No entanto, o {}in ${}é útil se você deseja expandir a variável foona string

"${foo}bar"

pois "$foobar"expandiria a variável identificada por foobar.

Chaves entre os dentes também são necessárias incondicionalmente quando:

  • expandindo elementos da matriz, como em ${array[42]}
  • usando operações de expansão de parâmetros, como em ${filename%.*}(remover extensão)
  • expandindo parâmetros posicionais além de 9: "$8 $9 ${10} ${11}"

Fazer isso em qualquer lugar, em vez de apenas em casos potencialmente ambíguos, pode ser considerado uma boa prática de programação. Isso serve tanto para consistência quanto para evitar surpresas $foo_$bar.jpg, onde não é visualmente óbvio que o sublinhado se torne parte do nome da variável.


106
{}é conhecido como expansão de chaves . ${}é conhecido como expansão variável. Eles fazem coisas diferentes. Eu o votaria com exceção do bit sem expansão.
Spencer Rathbun

5
@NewUser " Portanto, além de matrizes, isso não é realmente necessário ". Não, as chaves são necessárias para a EXPANSÃO DE PARÂMETROS , uma construção muito útil para scripts. Eu já vi muitos scripts sed e awk que podem ser substituídos por um pouco de expansão de parâmetros.
SiegeX

10
@caffinatedmonkey $()é usado para executar um comando, que md5sum=$(md5sum foo.bin)armazena a saída de md5sum foo.binna variável md5sum, agora acessível usando ${md5sum}. Além disso, +1 e muito mais em espírito de OP por mencionar que é uma boa prática ser explícito!
L0j1k

11
@ L0j1k Por falar em explicitação, acho importante mencionar que $()executa seu comando a partir de um subshell .
Adrian Günter

2
@karatedog ${1:-20}é uma forma de expansão de parâmetros. Aqui não é óbvio, porque utiliza principalmente dígitos e operadores aritméticos, o que nos engana ao pensar que há aritmética envolvida, mas na verdade se refere ao parâmetro posicional $1, que, se não definido, será substituído por um valor padrão 20(a sintaxe é ${variable:-default_value}).
Aaron

126

Variáveis ​​são declaradas e atribuídas sem $e sem {}. Você tem que usar

var=10

atribuir. Para ler a variável (em outras palavras, 'expanda' a variável), você deve usar $.

$var      # use the variable
${var}    # same as above
${var}bar # expand var, and append "bar" too
$varbar   # same as ${varbar}, i.e expand a variable called varbar, if it exists.

Isso me confundiu algumas vezes - em outros idiomas, nos referimos à variável da mesma maneira, independentemente de ela estar à esquerda ou à direita de uma tarefa. Mas o script de shell é diferente, $var=10não faz o que você pensa que faz!


34

Você usa {}para agrupar. As chaves são necessárias para remover a referência dos elementos da matriz. Exemplo:

dir=(*)           # store the contents of the directory into an array
echo "${dir[0]}"  # get the first entry.
echo "$dir[0]"    # incorrect

Eu não conseguia entender a primeira linha dir=(*). Tanto quanto eu sei, diré um comando embutido para listar o conteúdo do diretório (equivalente a ls -C -b). Você poderia explicar?
Jarvis

1
Na programação shell, comandos e argumentos devem ser separados um do outro por espaços em branco. Aqui, você vê o sinal de igual sem espaços em branco, o que significa que esta é uma atribuição de variável. diré o nome da variável e os parênteses são usados ​​para coletar a expansão do nome do arquivo *em uma matriz.
Glenn Jackman

27

Você também pode fazer alguma manipulação de texto dentro do aparelho:

STRING="./folder/subfolder/file.txt"
echo ${STRING} ${STRING%/*/*}

Resultado:

./folder/subfolder/file.txt ./folder

ou

STRING="This is a string"
echo ${STRING// /_}

Resultado:

This_is_a_string

Você está certo em "variáveis ​​regulares" não são necessárias ... Mas é mais útil para a depuração e a leitura de um script.


11

O final do nome da variável geralmente é representado por um espaço ou nova linha. Mas e se não quisermos um espaço ou uma nova linha depois de imprimir o valor da variável? Os colchetes indicam ao interpretador de shell onde está o final do nome da variável.

Exemplo clássico 1) - variável de shell sem espaço em branco à direita

TIME=10

# WRONG: no such variable called 'TIMEsecs'
echo "Time taken = $TIMEsecs"

# What we want is $TIME followed by "secs" with no whitespace between the two.
echo "Time taken = ${TIME}secs"

Exemplo 2) Caminho de classe Java com jars com versão

# WRONG - no such variable LATESTVERSION_src
CLASSPATH=hibernate-$LATESTVERSION_src.zip:hibernate_$LATEST_VERSION.jar

# RIGHT
CLASSPATH=hibernate-${LATESTVERSION}_src.zip:hibernate_$LATEST_VERSION.jar

(A resposta de Fred já afirma isso, mas o exemplo dele é um pouco abstrato demais)


5

Chaves sempre são necessárias para acessar os elementos da matriz e realizar a expansão da chave.

É bom não ter muito cuidado e usá-lo {}na expansão de variáveis ​​de shell, mesmo quando não há margem para ambiguidade.

Por exemplo:

dir=log
prog=foo
path=/var/${dir}/${prog}      # excessive use of {}, not needed since / can't be a part of a shell variable name
logfile=${path}/${prog}.log   # same as above, . can't be a part of a shell variable name
path_copy=${path}             # {} is totally unnecessary
archive=${logfile}_arch       # {} is needed since _ can be a part of shell variable name

Portanto, é melhor escrever as três linhas como:

path=/var/$dir/$prog
logfile=$path/$prog.log
path_copy=$path

o que é definitivamente mais legível.

Uma vez que um nome de variável não pode começar com um dígito, shell não precisa de {}variáveis em torno numeradas (como $1, $2etc.), a menos que tal expansão é seguido por um dígito. Isso é muito sutil e faz uso explícito {}em tais contextos:

set app      # set $1 to app
fruit=$1le   # sets fruit to apple, but confusing
fruit=${1}le # sets fruit to apple, makes the intention clear

Vejo:


1
It's good to be not over-cautious: Gostaria de saber o que a maioria das pessoas pensa. Use chaves o tempo todo, para não esquecê-las quando forem necessárias, ou usá-las somente quando necessário, para melhorar a legibilidade.
Roger Dahl

1
Eu acho que é a falta de consciência que leva os programadores a usar curlies, mesmo quando eles não são necessários. Essa ignorância é semelhante ao outro erro comum de não usar aspas duplas para impedir a divisão ou o globbing inadvertidos de palavras. Na base, a realidade é que os programadores não levam a sério os scripts de shell, assim como outras linguagens de script como Python e Ruby.
codeforester 16/04

1
Verdade isso. Minha irritação é que todo mundo parece pensar que todas as variáveis ​​devem estar em maiúsculas nos scripts de shell :)
Roger Dahl

2

Seguindo a sugestão de SierraX e Peter sobre manipulação de texto, colchetes {}são usados ​​para passar uma variável para um comando, por exemplo:

Digamos que você tenha um arquivo sposi.txt que contenha a primeira linha de um conhecido romance italiano:

> sposi="somewhere/myfolder/sposi.txt"
> cat $sposi

Ouput: quel ramo del lago di como che volge a mezzogiorno

Agora crie duas variáveis:

# Search the 2nd word found in the file that "sposi" variable points to
> word=$(cat $sposi | cut -d " " -f 2)

# This variable will replace the word
> new_word="filone"

Agora substitua a variável word content por new_word , dentro do arquivo sposi.txt

> sed -i "s/${word}/${new_word}/g" $sposi
> cat $sposi

Ouput: quel filone del lago di como che volge a mezzogiorno

A palavra "ramo" foi substituída.


1
Isso funciona tão bem sem chaves entre as variáveis.
Armali

Você pode querer corrigir o weel-known novelbit. Votado no entanto.
GSL
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.