diferença entre $ {} e $ () no shell script

Respostas:

39

$(command)é "substituição de comando". Como você parece entender, ele executa o command, captura sua saída e a insere na linha de comando que contém a $(…); por exemplo,

$ ls -ld $(date +%B).txt
-rwxr-xr-x  1 Noob Noob    867 Jul  2 11:09 July.txt

${parameter}é "substituição de parâmetro". Muitas informações podem ser encontradas na página de manual do shell, bash (1) , sob o cabeçalho “ Parameter Expansion ”:

${parameter}
    O valor do parâmetro é substituído. As chaves são necessárias quando parâmetro é um parâmetro posicional com mais de um dígito ou quando parâmetro é seguido por um caractere que não deve ser interpretado como parte de seu nome.

Para parâmetros posicionais, consulte " Parâmetros posicionais ", abaixo. Em seu uso mais comum, como mostrado nas outras respostas, parameteré um nome de variável. O ${…}formulário, conforme indicado no final do parágrafo acima, permite obter o valor de uma variável (ie ) e segui-lo imediatamente com uma letra, dígito ou sublinhado:$variable_name

$ animal = gato
$ echo $ animais
                                # Não existe variável como "animais".
$ echo $ {animal} s
gatos
$ echo $ animal_food
                                # Não existe variável como "animal_food".
$ eco $ {animal} _alimento
comida de gato

Você também pode fazer isso com aspas:

$ echo "$ animal" s
gatos

Ou, como exercício de opções, você pode usar uma segunda variável:

$ plural = s
$ echo $ animal $ plural
gatos

Mas este é apenas o passo 1. O próximo parágrafo da página de manual é interessante, embora um pouco enigmático:

Se o primeiro caractere do parâmetro for um ponto de exclamação ( !), é introduzido um nível de indireção variável. Bash usa o valor da variável formada a partir do restante do parâmetro como o nome da variável; essa variável é expandida e esse valor é usado no restante da substituição, em vez do valor do próprio parâmetro . Isso é conhecido como expansão indireta .     (Exceções)    O ponto de exclamação deve seguir imediatamente a chave esquerda para introduzir a indireção.

Não tenho certeza de como posso deixar isso mais claro, exceto pelo exemplo:

$ animal = gato
$ echo $ animal
gato
$ cat = tabby
$ echo $ cat
malhado
$ echo $ {! animal}
tabby                            # Se $ animal for "gato" , $ {! animal} será $ cat , ou seja, "gato malhado"

Então, vamos chamar essa etapa de 1½. Há muitas coisas interessantes que você pode fazer na etapa 2:

$ animal = gato
$ echo $ {# animal}Comprimento da corda 
3                                #
$ echo $ {animal / at / ow}
cow                              # Substitution

Você não pode fazer nenhuma dessas coisas sem o {... }aparelho.

Parâmetros posicionais

Considere este exemplo artificial :

$ cat myecho.sh
eco $ 1 $ 2 $ 3 $ 4 $ 5 $ 6 $ 7 $ 8 $ 9 $ 10 $ 11 $ 12 $ 13 $ 14 $ 15
$ ./myecho.sh Hey, diddle diddle, O gato e o rabeca, A vaca saltou sobre a lua.
Hey diddle diddle, O gato e o rabeca, O Hey0 Hey1 Hey2 Hey2 Hey3 Hey4 Hey5

porque o shell não entende $10, $11etc. Trata-se $10, como se fosse ${1}0. Mas ele entende ${10}, ${11}etc., conforme mencionado na página do manual ("um parâmetro posicional com mais de um dígito").

Mas não escreva scripts como esse; existem maneiras melhores de lidar com longas listas de argumentos.

O exposto acima (junto com muitas outras formas de construções) é discutido em maior detalhe na página de manual do shell, bash (1) .${parameter…something_else}

Uma nota sobre cotações

Observe que você sempre deve citar variáveis ​​do shell, a menos que tenha um bom motivo para não fazê-lo , e tem certeza de que sabe o que está fazendo. Por outro lado, enquanto os aparelhos podem ser importantes, eles não são tão importantes quanto as aspas.

$ filename = "berçário rhyme.txt"
$ ls -ld $ {nome do arquivo}
ls: não pode acessar o berçário: nenhum arquivo ou diretório
ls: não é possível acessar rhyme.txt: não existe esse arquivo ou diretório
$ ls -ld "$ filename"
-rwxr-xr-x 1 Noob Noob 5309 2 de julho às 11:09 nursery rhyme.txt

Isso também se aplica a parâmetros posicionais (isto é, argumentos de linha de comando; por exemplo "$1") e também a substituição de comandos:

$ ls -ld $ (data "+% B% Y"). txt
ls: não pode acessar julho: esse arquivo ou diretório não existe
ls: não é possível acessar 2015.txt: não existe esse arquivo ou diretório
$ ls -ld "$ (data" +% B% Y "). txt"
-rwxr-xr-x 1 Noob Noob 687 2 de julho 11:09 de julho de 2015.txt

Consulte Citações de Bash sem escape na substituição de comandos para um breve tratado sobre a interação entre aspas e $().

G-Man diz que 'restabelece Monica'
fonte
obrigado pelo exemplo maravilhoso. Você pode elaborar o uso de! no seu exemplo. Eu realmente não entendo como isso funciona.
Noob
@ Noob: OK, eu expliquei sobre o uso de !.
G-Man diz 'Reinstate Monica'
obrigado. De alguma forma, animal está realmente se referindo à variável gato em vez do valor gato. Você também pode me informar como / at se torna um "c" em eco $ {animal / at / ow} A explicação do homem sobre bash é de alguma forma ... eu não sei. dificíl de entender.
Noob
Além disso, você pode elaborar esta frase - "$ {parameter} O valor do parâmetro é substituído." substituído com o que? se o parâmetro for fruit e seu valor for apple - fruit = apple, então $ {fruit} - apple será substituída por ?? realmente não obter o significado aqui substituído
Noob
@Noob: “ ${!animal}está realmente se referindo à variável em $catvez do valor cat.” Sim, esse é exatamente o ponto. “Como / at se torna um“ c ”em eco $ {animal / at / ow}?” Hein? / at não se torna "c"; "Gato" se torna "vaca" quando "em" é substituído por "ow".
G-Man diz 'Reinstate Monica'
7

No seu exemplo, $ var e $ {var} são idênticos. No entanto, chaves entre colchetes são úteis quando você deseja expandir a variável em uma string:

    $ string=foo
    $ echo ${string}bar
      foobar
    $ echo $stringbar

    $ 

Assim, os chavetas fornecem um meio de substituir a variável para que o nome da nova variável seja substituída.

MariusMatutiae
fonte
4

Eu costumo vê-lo mais comumente em strings. Algo assim não funcionará:

var="a"
echo "$varRAW_STRING"

Mas isso irá:

var="a"
echo "${var}RAW_STRING"

Como você disse corretamente, $()é usado para executar um comando:

dir_contents=$(ls)

Você também pode usar backticks, mas acho o $()mais versátil. Por um lado, os backticks não podem (facilmente) ser aninhados.

date_directory=`ls `date '+%Y-%m-%d'`` # Makes no sense
date_directory=$(ls $(date '+%Y-%m-%d')) # Much better
bytes
fonte
Na verdade, backticks pode ser aninhada: date_directory=`ls \`date '+%Y-%m-%d'\``. Mas é terrivelmente feio; $(…)é muito mais claro e fácil de usar.
G-Man diz 'Reinstate Monica'
Ok, é justo. É tecnicamente possível, mas eu não posso imaginar alguém que nunca fazê-lo apenas por causa da legibilidade
bytesized
1
Bem, `…`foi a (única) sintaxe da substituição de comando anos antes que $(…)foi inventada, então você não precisa imaginar nada - as pessoas fizeram isso.
G-Man diz 'Reinstate Monica'
s / sempre fazendo / fazendo mais /
bytesized