Um conselho que quem está começando a programar em bash vai ouvir repetidas vezes:
Coloque suas variáveis entre aspas!
Por quê?
Quando você expande uma variável (como $name), quase sempre quer usar o valor da variável como uma única string (ou número).
Você não quer que o valor seja tratado inesperadamente como duas ou mais strings separadas.
O uso de aspas é o mecanismo principal para garantir que um trecho de texto seja tratado como uma única palavra. Existem dois tipos principais de aspas.
As aspas duplas permitem expansão de parâmetro, expansão aritmética e substituição de comando dentro delas.
echo "This is bash version $BASH_VERSION"
Você pode incluir uma aspa dupla literal dentro de uma string entre aspas duplas colocando uma barra invertida antes dela.
echo "She said \"Let me teach you about bash.\""
As aspas simples não permitem nenhuma expansão dentro delas.
echo 'This is bash version $BASH_VERSION'
# ...^..................................^
# prints exactly: This is bash version $BASH_VERSION
Não é possível embutir uma aspa simples dentro de uma string entre aspas simples.
O que há de errado com echo $name, exatamente?
Quando o shell interpreta uma linha de código, ele realiza vários tipos de expansão, em ordem. Duas das últimas expansões são a Divisão de Palavras e a Expansão de Nomes de Arquivos.
Quaisquer resultados de expansão de parâmetro, substituição de comando ou expansão aritmética que não ocorreram dentro de aspas duplas são candidatos à "divisão de palavras".
O shell pega o resultado expandido e o divide em qualquer sequência de caracteres presente no valor de $IFS.
Por padrão, isso é espaço, tabulação e quebra de linha.
Podemos ver isso em ação usando um laço do shell:
sentence="The quick brown fox jumps."
i=0
for word in "$sentence"; do echo "$((++i)) $word"; done
# ..........^.........^
produz
1 The quick brown fox jumps.
Mas sem aspas:
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^
produz
1 The
2 quick
3 brown
4 fox
5 jumps.
Vamos ver com um valor diferente de $IFS:
csv="first,second,third"
IFS=","
for word in $csv; do echo "$word"; done
produz
first
second
third
Uma técnica para desativar a divisão de palavras é definir $IFS como uma string vazia
sentence="The quick brown fox jumps."
IFS= # or explicitly, IFS=""
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^
produz
1 The quick brown fox jumps.
No entanto, deixar variáveis sem aspas não é recomendado, a menos que você também desative a Expansão de Nomes de Arquivos. Vamos ver como fazer isso mais adiante.
Os padrões "glob" são usados para expressar um padrão conciso que corresponde a um conjunto de arquivos.
Por exemplo, você pode corresponder a todos os arquivos Markdown de um diretório com *.md.
Os padrões glob existem desde a invenção do Unix.
Os caracteres curinga do glob são:
* corresponde a zero ou mais de qualquer caractere,
? corresponde a exatamente um de qualquer caractere,
[...] corresponde a exatamente um caractere do conjunto de caracteres listados dentro dos colchetes.
[abc] corresponde a exatamente um caractere que seja um a, um b ou um c.
^ opcional)
[][] é um conjunto de caracteres que corresponde a um colchete de fechamento ou de abertura.[[\]]
[x-y] corresponde a exatamente um caractere que esteja no intervalo de x a y
^ opcional) ou o último caractere antes do colchete de fechamento.
[-xy] ou [xy-] correspondem, cada um, a exatamente um caractere que seja um -, um x ou um y
[x\-y]
[0-9A-Fa-f] corresponde a um dígito hexadecimal[^abc] corresponde a exatamente um caractere que NÃO seja um a, um b ou um c,[^x-y] corresponde a exatamente um caractere que NÃO esteja no intervalo de x a y
[[:character_class:]] corresponde a um caractere que pertence à "character_class" indicada
lower -- letras minúsculasupper -- letras maiúsculasalpha -- letrasdigit -- dígitosalnum -- letras e dígitosspace -- espaços em brancoblank -- espaços em branco horizontais (espaço e tabulação)xdigit -- dígitos hexadecimaisword -- caracteres permitidos em um identificador (letras, números, sublinhado)punct -- pontuaçãocntrl -- caracteres de controlegraph -- caracteres "visíveis"print -- caracteres visíveis mais espaço[[:alpha:]] é composta de [[:lower:][:upper:]]
[[:alnum:]] é composta de [[:alpha:][:digit:]]
[[:word:]] é composta de [[:alnum:]_]
[[:word:]] quanto [[:punct:]]
[[:graph:]] é composta de [[:alnum:][:punct:]]
[[:print:]] é composta de [[:graph:] ] -- apenas espaço (octal 040), e não qualquer outro espaço em branco.Você pode iterar sobre uma lista de arquivos com um laço for
for file in *.csv; do
do_something_with "$file"
done
Repare que a variável "$file" está entre aspas.
O padrão glob pode retornar arquivos que contêm espaços no nome.
Precisamos colocar a variável entre aspas para que o comando do_something_with receba exatamente um argumento, o nome do arquivo.
Padrões glob não são expressões regulares.
.
?
.*
*
.* significa "corresponder a um nome de arquivo em que o primeiro caractere é um ponto literal seguido de zero ou mais de qualquer caractere"..+
?*
O Bash oferece padrões estendidos que são mais próximos de expressões regulares.
Há momentos em que você quer suprimir a expansão de nomes de arquivos. Lidar com a entrada do usuário é um desses momentos. Considere este trecho de código
read -p "Enter something: " user_input
echo $user_input
Se o usuário digitar um asterisco (*), o que será exibido?
A lista de arquivos do diretório atual.
Provavelmente não é isso que você quer fazer.
Outro exemplo é passar padrões como argumentos para programas que esperam padrões.
O programa tr é frequentemente usado para manipulação de texto. Para transformar o valor de uma variável em minúsculas, você pode fazer isto
var="My Puppet"
echo "$var" | tr '[:upper:]' '[:lower:]'
produz
my puppet
Um erro comum é esquecer de colocar entre aspas os argumentos do tr: tr [:upper:] [:lower:]
Por que isso é um problema?
Padrões glob sem aspas serão expandidos para a lista de arquivos correspondentes.
Suponha que a pessoa que usa seu script tenha arquivos chamados p, u e w no diretório atual:
touch p u w
var="My Puppet"
echo "$var" | tr [:upper:] [:lower:]
Isso resulta em um erro!
O padrão [:upper:] sem aspas corresponde a qualquer um de :, u, p, e ou r.
O padrão [:lower:] sem aspas corresponde a qualquer um de :, l, o, w, e ou r.
O Bash corresponde aos arquivos p e u para o primeiro padrão, e ao arquivo w para o segundo, e o tr é invocado assim
echo "$var" | tr p u w
E esse é o número errado de argumentos para o tr.
Você pode usar o comando set para desativar a expansão de nomes de arquivos.
echo * # prints a list of files
set -f # disable filename expansion
echo * # prints a literal asterisk
set +f # enable filename expansion
Há várias configurações do comando interno shopt que controlam como a expansão de nomes de arquivos funciona.
Algumas interessantes são:
shopt -s nocaseglob -- faz correspondência sem diferenciar maiúsculas de minúsculasshopt -s extglob -- ativa os padrões estendidosshopt -s nullglob -- se nenhum arquivo corresponder, substitui o padrão por nada.
O comportamento padrão é deixar o padrão no lugar como uma string literal.