$ VAR vs $ {VAR} e para citar ou não

139

eu consigo escrever

VAR=$VAR1
VAR=${VAR1}
VAR="$VAR1"
VAR="${VAR1}"

o resultado final para mim tudo parece o mesmo. Por que devo escrever um ou outro? algum destes não é portátil / POSIX?

xenoterracida
fonte

Respostas:

99

VAR=$VAR1é uma versão simplificada do VAR=${VAR1}. Há coisas que o segundo pode fazer e o primeiro não, por exemplo, fazer referência a um índice de matriz (não portátil) ou remover uma substring (POSIX-portable). Consulte a seção Mais sobre variáveis do Guia Bash para iniciantes e expansão de parâmetros na especificação POSIX.

Usar aspas em torno de uma variável como em rm -- "$VAR1"ou rm -- "${VAR}"é uma boa ideia. Isso torna o conteúdo da variável uma unidade atômica. Se o valor da variável contiver espaços em branco (bem, caracteres na $IFSvariável especial, espaços em branco por padrão) ou caracteres brilhantes e você não os citar, cada palavra será considerada para geração de nome de arquivo (globbing) cuja expansão gera tantos argumentos para o que você quiser. está fazendo.

$ find .
.
./*r*
./-rf
./another
./filename
./spaced filename
./another spaced filename
./another spaced filename/x
$ var='spaced filename'
# usually, 'spaced filename' would come from the output of some command and you weren't expecting it
$ rm $var
rm: cannot remove 'spaced': No such file or directory
# oops! I just ran 'rm spaced filename'
$ var='*r*'
$ rm $var
# expands to: 'rm' '-rf' '*r*' 'another spaced filename'

$ find .
.
./another
./spaced filename
./another spaced filename
$ var='another spaced filename'
$ rm -- "$var"
$ find .
.
./another
./spaced filename

Sobre portabilidade: De acordo com a seção 2.6.2 do POSIX.1-2008 , as chaves são opcionais.

Shawn J. Goff
fonte
@shawn atualizou minha pergunta, porque também tenho curiosidade sobre portabilidade.
xenoterracide
@ Shawn: duvido que seu exemplo seja válido. Você tem algum exemplo real de um shell em que a var1=$varexpansão gera um erro?
22410 alex
@ alex: Obrigado. Eu pensei que tinha testado isso na linha de comando, mas fiz errado. Eu mudei o exemplo.
Shawn J. Goff
Com o exemplo atualizado, é melhor ter em mente que geralmente você deseja a versão citada, pois o exemplo é um caso de esquina.
alex
9
@ Shawn: as cotações não são necessárias em uma tarefa. Eles são necessários na maioria dos outros usos, inclusive export VAR=$VAR1. Quanto aos aparelhos, eles são opcionais (verifique o quarto parágrafo da seção que você citou; este é o caso em todos os shells pré-POSIX e POSIX).
Gilles
60

${VAR}e $VARsão exatamente equivalentes. Para uma expansão de variável simples, o único motivo para usar ${VAR}é quando a análise capturaria muitos caracteres no nome da variável, como em ${VAR1}_$VAR2(que sem chaves seria equivalente a ${VAR1_}$VAR2). Expansões mais enfeitadas ( ${VAR:=default}, ${VAR#prefix}...) exigem chaves.

Em uma atribuição de variável, a divisão de campo (ou seja, divisão em espaço em branco no valor) e a expansão do nome do caminho (ou seja, globbing) são desativadas, portanto VAR=$VAR1é exatamente equivalente a VAR="$VAR1", em todos os shells POSIX e em todos os sh pré-POSIX que eu ouvi falar . (Ref POSIX: comandos simples ). Pelo mesmo motivo, VAR=*define de forma confiável VARa string literal *; é claro que VAR=a bdefine VARcomo adesde que bé uma palavra separada em primeiro lugar. De um modo geral, aspas duplas são desnecessárias onde a sintaxe do shell espera uma única palavra, por exemplo emcase … in (mas não no padrão), mas mesmo assim é preciso ter cuidado: por exemplo, POSIX especifica queos destinos de redirecionamento ( >$filename) não exigem aspas nos scripts, mas alguns shells, incluindo o bash, exigem aspas duplas, mesmo nos scripts. Consulte Quando é necessário citar duas vezes? para uma análise mais completa.

Você precisa de aspas duplas em outros casos, especialmente em export VAR="${VAR1}"(que pode ser escrito de forma equivalente export "VAR=${VAR1}") em muitos shells (o POSIX deixa esse caso em aberto). A semelhança desse caso com atribuições simples e a natureza dispersa da lista de casos em que você não precisa de aspas duplas são por isso que recomendo usar aspas duplas, a menos que você queira dividir e globar.

Gilles
fonte
2
Como regra geral, sempre cito expansões de variáveis, mesmo quando sei que o valor não conterá nenhum IFScaractere porque quero ter o hábito. A única exceção é que não cito o valor ao fazer uma atribuição de variável (a menos que seja necessário, como quando o valor contém um espaço). Isso torna o destaque da sintaxe do editor mais útil quando há substituições de comandos como FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}"). Em vez de colorir tudo a cor "string", recebo o destaque da sintaxe do código aninhado. É também por isso que evito backticks.
Richard Hansen ''
Embora >$fileesteja bom nos scripts POSIX, ele não está no bash, mesmo quando não interativo (a menos que a conformidade com POSIX seja imposta $POSIXLY_CORRECTou --posix...).
Stéphane Chazelas
Embora seja verdade que aspas não são necessárias VAR=$VAR1, às vezes me surpreendo local VAR=$VAR1, e lembro-me de trabalhar de maneira diferente em alguns aspectos, em pelo menos algumas conchas. Mas atm, não posso reproduzir a divergência.
precisa
Ok, encontrei o problema que eu estava lembrando . Ele só aparece em algumas conchas.
dubiousjim
@dubiousjim local VAR=$VAR1é como export VAR=$VAR1, depende do shell.
Gilles
8

cotação

Considere que aspas duplas são usadas para expansão variável e aspas simples são usadas para aspas fortes, ou seja, sem expansão.

Expansão:

this='foo'
that='bar'
these="$this"
those='$that'

Resultado:

for item in "$this" "$that" "$these" "$those"; do echo "$item"; done
foo
bar
foo
$that

Vale a pena mencionar que você deve usar a cotação sempre que possível por vários motivos, dentre os melhores dentre os quais é considerado melhor prática e para facilitar a leitura. Também porque o Bash é peculiar às vezes e freqüentemente por maneiras aparentemente ilógicas ou irracionais / inesperadas, e a cotação muda as expectativas implícitas para explícitas, o que reduz a superfície do erro (ou o potencial para isso).

Embora seja totalmente legal não citar e funcione na maioria dos casos, essa funcionalidade é fornecida por conveniência e provavelmente é menos portátil. a prática totalmente formal garantida para refletir intenção e expectativa é citar.

Substituição

Agora considere também que a construção "${somevar}"é usada para operações de substituição. Vários casos de uso, como substituição e matrizes.

Substituição (decapagem):

thisfile='foobar.txt.bak'
foo="${thisfile%.*}"   # removes shortest part of value in $thisfile matching after '%' from righthand side
bar="${thisfile%%.*}"  # removes longest matching

for item in "$foo" "$bar"; do echo "$item"; done
foobar.txt
foobar

Substituição (substituição):

foobar='Simplest, least effective, least powerful'
# ${var/find/replace_with}
foo="${foobar/least/most}"   #single occurrence
bar="${foobar//least/most}"  #global occurrence (all)

for item in "$foobar" "$foo" "$bar"; do echo "$item"; done
Simplest, least effective, least powerful
Simplest, most effective, least powerful
Simplest, most effective, most powerful

Matrizes:

mkdir temp
# create files foo.txt, bar.txt, foobar.txt in temp folder
touch temp/{foo,bar,foobar}.txt
# alpha is array of output from ls  
alpha=($(ls temp/*))

echo "$alpha"         #  temp/foo.txt
echo "${alpha}"       #  temp/foo.txt
echo "${alpha[@]}"    #  temp/bar.txt  temp/foobar.txt  temp/foo.txt
echo "${#alpha}"      #  12 # length of first element (implicit index [0])
echo "${#alpha[@]}"   #  3  # number of elements
echo "${alpha[1]}"    #  temp/foobar.txt # second element
echo "${#alpha[1])"   #  15 # length of second element

for item in "${alpha[@]}"; do echo "$item"; done
temp/bar.txt
temp/foobar.txt
temp/foo.txt

Tudo isso está apenas arranhando a superfície da "${var}"construção de substituição. A referência definitiva para scripts de shell Bash é a referência on-line gratuita, TLDP The Linux Documentation Projecthttps://www.tldp.org/LDP/abs/html/parameter-substitution.html

SYANiDE
fonte
1
muito informativo.
orion elenzil 20/03
0
ls -la

lrwxrwxrwx.  1 root root      31 Nov 17 13:13 prodhostname
lrwxrwxrwx.  1 root root      33 Nov 17 13:13 testhostname
lrwxrwxrwx.  1 root root      32 Nov 17 13:13 justname

fim então:

env=$1
    if [ ! -f /dirname/${env}hostname ]

vale a pena mencionar como um exemplo mais claro do uso de curvas

ninjabber
fonte