Alguém poderia me dizer se devo ou não colocar aspas em torno de variáveis em um script de shell?
Por exemplo, o seguinte está correto:
xdg-open $URL
[ $? -eq 2 ]
ou
xdg-open "$URL"
[ "$?" -eq "2" ]
E se sim, por quê?
Alguém poderia me dizer se devo ou não colocar aspas em torno de variáveis em um script de shell?
Por exemplo, o seguinte está correto:
xdg-open $URL
[ $? -eq 2 ]
ou
xdg-open "$URL"
[ "$?" -eq "2" ]
E se sim, por quê?
Respostas:
Regra geral: cite se pode estar vazio ou conter espaços (ou qualquer espaço em branco) ou caracteres especiais (curingas). Não citar cadeias de caracteres com espaços geralmente leva o shell a dividir um único argumento em muitos.
$?
não precisa de aspas, pois é um valor numérico. Se $URL
precisa, depende do que você permite e se ainda deseja uma discussão, se estiver vazia.
Costumo sempre citar strings apenas por hábito, pois é mais seguro assim.
IFS=0
, então echo $?
pode ser muito surpreendente.
cp $source1 $source2 $dest
, mas se por algum motivo inesperado dest
não for definido, o terceiro argumento simplesmente desaparecerá e ele será copiado silenciosamente, source1
em source2
vez de fornecer um erro apropriado para o destino em branco (como teria se você tivesse citado cada argumento).
quote it if...
tem o processo de pensamento para trás - as aspas não são algo que você adiciona quando precisa, são algo que você remove quando precisa. Sempre envolver cordas e scripts entre aspas simples a menos que você precisa para usar aspas duplas (por exemplo, para permitir que uma variável expandir) ou necessidade de usar sem aspas (por exemplo, para fazer englobamento eo nome do arquivo de expansão).
Em resumo, cite tudo onde você não precisa que o shell execute a divisão de token e a expansão de curinga.
Aspas simples protegem o texto entre elas literalmente. É a ferramenta adequada quando você precisa garantir que o shell não toque na corda. Normalmente, é o mecanismo de citação preferido quando você não requer interpolação variável.
$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change
$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.
Aspas duplas são adequadas quando é necessária interpolação variável. Com adaptações adequadas, também é uma boa solução alternativa quando você precisa de aspas simples na string. (Não existe uma maneira simples de escapar de uma citação única entre aspas simples, porque não há mecanismo de escape dentro de aspas simples - se houvesse, elas não citariam completamente textualmente.)
$ echo "There is no place like '$HOME'"
There is no place like '/home/me'
Nenhuma cotação é adequada quando você exige especificamente que o shell execute a divisão de token e / ou expansão de curinga.
Divisão de token;
$ words="foo bar baz"
$ for word in $words; do
> echo "$word"
> done
foo
bar
baz
Por contraste:
$ for word in "$words"; do echo "$word"; done
foo bar baz
(O loop é executado apenas uma vez, sobre a única cadeia de caracteres citada.)
$ for word in '$words'; do echo "$word"; done
$words
(O loop é executado apenas uma vez, sobre a string literal entre aspas.)
Expansão curinga:
$ pattern='file*.txt'
$ ls $pattern
file1.txt file_other.txt
Por contraste:
$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory
(Não há arquivo nomeado literalmente file*.txt
.)
$ ls '$pattern'
ls: cannot access $pattern: No such file or directory
(Não há arquivo chamado $pattern
!)
Em termos mais concretos, qualquer coisa que contenha um nome de arquivo geralmente deve ser citada (porque os nomes de arquivos podem conter espaços em branco e outros metacaracteres do shell). Qualquer coisa que contenha uma URL geralmente deve ser citada (porque muitas URLs contêm metacaracteres de shell como ?
e&
). Qualquer coisa que contenha uma regex geralmente deve ser citada (idem idem). Qualquer coisa que contenha espaços em branco significativos que não sejam espaços únicos entre caracteres que não sejam espaços em branco precisa ser citada (porque, caso contrário, o shell moverá o espaço em branco para, efetivamente, espaços únicos e aparará qualquer espaço em branco à esquerda ou à direita).
Quando você sabe que uma variável pode conter apenas um valor que não contém metacaracteres de shell, a citação é opcional. Assim, um não citado $?
é basicamente bom, porque essa variável só pode conter um único número. Contudo,"$?"
também é correto e recomendado para consistência e correção gerais (embora essa seja minha recomendação pessoal, não uma política amplamente reconhecida).
Os valores que não são variáveis seguem basicamente as mesmas regras, embora você também possa escapar de qualquer metacaractere em vez de citá-los. Para um exemplo comum, um URL com um &
in será analisado pelo shell como um comando em segundo plano, a menos que o metacaractere seja escapado ou citado:
$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found
(Obviamente, isso também acontece se o URL estiver em uma variável sem aspas.) Para uma sequência estática, aspas simples fazem mais sentido, embora qualquer forma de citação ou escape funcione aqui.
wget 'http://example.com/q&uack' # Single quotes preferred for a static string
wget "http://example.com/q&uack" # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack # Backslash escape
wget http://example.com/q'&'uack # Only the metacharacter really needs quoting
O último exemplo também sugere outro conceito útil, que eu gosto de chamar de "gangorra citando". Se você precisar misturar aspas simples e duplas, poderá usá-las adjacentes uma à outra. Por exemplo, as seguintes seqüências de caracteres citadas
'$HOME '
"isn't"
' where `<3'
"' is."
podem ser colados juntos, seguidos, formando uma única sequência longa após a remoção de tokenização e cotação.
$ echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.
Isso não é muito legível, mas é uma técnica comum e, portanto, é bom saber.
Como um aparte, os scripts geralmente não devem ser usados ls
para nada. Para expandir um curinga, apenas ... use-o.
$ printf '%s\n' $pattern # not ``ls -1 $pattern''
file1.txt
file_other.txt
$ for file in $pattern; do # definitely, definitely not ``for file in $(ls $pattern)''
> printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt
(O loop é completamente supérfluo no último exemplo; printf
funciona especificamente bem com vários argumentos.stat
. Mas repetir uma correspondência curinga é um problema comum e frequentemente incorreto.)
Uma variável que contém uma lista de tokens a serem repetidos ou um curinga a ser expandido é vista com menos frequência; portanto, às vezes abreviamos para "citar tudo, a menos que você saiba exatamente o que está fazendo".
Aqui está uma fórmula de três pontos para citações em geral:
Aspas duplas
Em contextos em que queremos suprimir a divisão de palavras e globbing. Também em contextos em que queremos que o literal seja tratado como uma sequência, não como uma expressão regular.
Citações simples
Em literais de cadeia de caracteres onde queremos suprimir a interpolação e tratamento especial de barras invertidas. Em outras palavras, situações em que o uso de aspas duplas seriam inapropriadas.
Sem aspas
Em contextos em que temos certeza absoluta de que não há problemas de divisão ou globbing de palavras, ou queremos a divisão e globbing de palavras .
Exemplos
Aspas duplas
"StackOverflow rocks!"
, "Steve's Apple"
)"$var"
, "${arr[@]}"
)"$(ls)"
, "`ls`"
)"/my dir/"*
)"single'quote'delimited'string"
)"${filename##*/}"
)Citações simples
'Really costs $$!'
, 'just a backslash followed by a t: \t'
)'The "crux"'
)$'\n\t'
)$'{"table": "users", "where": "first_name"=\'Steve\'}'
)Sem aspas
$$
, $?
, $#
etc.)((count++))
, "${arr[idx]}"
,"${string:start:length}"
[[ ]]
expressão interna isenta de divisão de palavras e problemas de globbing (isso é uma questão de estilo e as opiniões podem variar bastante)for word in $words
)for txtfile in *.txt; do ...
)~
ser interpretados como $HOME
( ~/"some dir"
mas não "~/some dir"
)Veja também:
"ls" "/"
A frase "todos os contextos de string" precisa ser qualificada com mais cuidado.
[[ ]]
, citar importa no lado direito de =
/ ==
e =~
: faz a diferença entre interpretar uma string como um padrão / regex ou literalmente.
$'...'
.
"ls" "/"
vez das mais comuns ls /
, e considero isso uma falha importante nas diretrizes.
case
:)
Eu geralmente uso citado como "$var"
para seguro, a menos que tenha certeza de que $var
não contém espaço.
Eu uso $var
como uma maneira simples de juntar linhas:
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped
Para usar as variáveis no script de shell, use as variáveis citadas "" como a citada significa que a variável pode conter espaços ou caracteres especiais que não afetarão a execução do seu script de shell. Caso contrário, se você tiver certeza de não ter espaços ou caracteres especiais no nome da sua variável, poderá usá-los sem "".
Exemplo:
eco "$ url name" - (pode ser usado o tempo todo)
eco "$ url name" - (não pode ser usado em tais situações, tome cuidado antes de usá-lo)