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

20
$ echo $(date)
Thu Jul 2 16:33:11 SGT 2015
$ echo ${date}

$ name=foo
$ echo $(name)
ksh: name:  not found

$ echo ${name}
foo

Parece que $ {variable} é o mesmo que $ variable. enquanto $ () é para executar um comando. Por que usar $ {} então?

    
por Noob 02.07.2015 / 10:37

3 respostas

26

$(command) é “substituição de comando”. Como você parece entender, ele executa o command , captura sua saída, e insere isso na linha de comando que contém o $(…) ; 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 título " Expansão do Parâmetro ":

${parameter}
    The value of parameter is substituted.  The braces are required when parameter is a positional parameter with more than one digit, or when parameter is followed by a character which is not to be interpreted as part of its name.

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. A forma ${…} , como indicado no final do parágrafo acima, permite que você obtenha o valor de uma variável (por exemplo, $variable_name ) e siga imediatamente com uma letra, um dígito ou um sublinhado:

$ animal=cat
$ echo $animals
                                # No such variable as “animals”.
$ echo ${animal}s
cats
$ echo $animal_food
                                # No such variable as “animal_food”.
$ echo ${animal}_food
cat_food

Você também pode fazer isso com aspas:

$ echo "$animal"s
cats

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

$ plural=s
$ echo $animal$plural
cats

Mas isso é apenas o passo 1. O próximo parágrafo na página man é interessante, embora um pouco enigmático:

If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced.  Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself.  This is known as indirect expansion.     (exceptions)   The exclamation point must immediately follow the left brace in order to introduce indirection.

Não sei como posso esclarecer isso, exceto pelo exemplo:

$ animal=cat
$ echo $animal
cat
$ cat=tabby
$ echo $cat
tabby
$ echo ${!animal}
tabby                           # If $animal is “cat”, then ${!animal} is $cat, i.e., “tabby”

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

$ animal=cat
$ echo ${#animal}
3                               # String length
$ echo ${animal/at/ow}
cow                             # Substitution

Você não pode fazer nada disso sem as chaves {} .

Parâmetros posicionais

Considere este exemplo artificial :

$ cat myecho.sh
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15
$ ./myecho.sh Hey diddle diddle, The cat and the fiddle, The cow jumped over the moon.
Hey diddle diddle, The cat and the fiddle, The Hey0 Hey1 Hey2 Hey3 Hey4 Hey5

porque o shell não entende $10 , $11 etc. Ele trata $10 como se fosse ${1}0 . Mas ele entende ${10} , ${11} , etc., como mencionado na man page (“Um parâmetro posicional com mais de um dígito”).

But don’t actually write scripts like that; there are better ways to deal with long argument lists.

O acima (junto com muitas outras formas de ${parameter…something_else} constructos) são discutidos mais detalhadamente na man page do shell, bash (1) .

Uma nota sobre as cotações

Note que você deve sempre citar as variáveis do shell a menos que você tenha uma boa razão para não e você está certo de que sabe o que está fazendo. Por outro lado, enquanto chaves podem ser importantes, eles não são tão importantes quanto as citações.

$ filename="nursery rhyme.txt"
$ ls -ld ${filename}
ls: cannot access nursery: No such file or directory
ls: cannot access rhyme.txt: No such file or directory
$ ls -ld "$filename"
-rwxr-xr-x  1 Noob Noob   5309 Jul  2 11:09 nursery rhyme.txt

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

$ ls -ld $(date "+%B %Y").txt
ls: cannot access July: No such file or directory
ls: cannot access 2015.txt: No such file or directory
$ ls -ld "$(date "+%B %Y").txt"
-rwxr-xr-x  1 Noob Noob    687 Jul  2 11:09 July 2015.txt

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

    
por 02.07.2015 / 13:14
6

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

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

    $ 

Assim, as chaves fornecem um meio de substituir a variável, a fim de obter o nome da nova variável, a ser substituída.

    
por 02.07.2015 / 10:45
3

Eu geralmente vejo isso mais comumente em strings. Algo parecido com isso não funcionará:

var="a"
echo "$varRAW_STRING"

Mas isso vai:

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 a $() mais versátil. Por um lado, os backticks não podem ser (facilmente) aninhados.

date_directory='ls 'date '+%Y-%m-%d''' # Makes no sense
date_directory=$(ls $(date '+%Y-%m-%d')) # Much better
    
por 02.07.2015 / 10:40