Trilhas
/
Bash
Bash
/
Programa
/
A importância das aspas
A

A importância das aspas em Bash

{one: "1 exercício", many: "%{count} de exercícios", other: "%{count} exercícios"}

Sobre A importância das aspas

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.

Uso de aspas

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.

Aspas duplas

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.\""

Aspas simples

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.

A importância do uso de aspas

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.

Divisão de Palavras

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
Desativar a divisão de palavras

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.

Expansão de Nomes de Arquivos

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.

    Advanced
    Há muitos detalhes sobre conjuntos de caracteres. Clique aqui para mostrá-los.
    • [abc] corresponde a exatamente um caractere que seja um a, um b ou um c.
      • para incluir um colchete de fechamento literal na expressão, faça dele o primeiro caractere depois do colchete de abertura (depois de um ^ opcional)
        • [][] é um conjunto de caracteres que corresponde a um colchete de fechamento ou de abertura.
        • ou escape-o com uma barra invertida: [[\]]
    • [x-y] corresponde a exatamente um caractere que esteja no intervalo de x a y
      • para incluir um caractere de hífen literal em um conjunto de caracteres, ele deve ser o primeiro caractere depois do colchete de abertura (depois de um ^ 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
        • ou escape-o com uma barra invertida: [x\-y]
      • vários intervalos podem ser especificados, por exemplo [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
      • as classes de caracteres disponíveis são:
        • lower -- letras minúsculas
        • upper -- letras maiúsculas
        • alpha -- letras
        • digit -- dígitos
        • alnum -- letras e dígitos
        • space -- espaços em branco
        • blank -- espaços em branco horizontais (espaço e tabulação)
        • xdigit -- dígitos hexadecimais
        • word -- caracteres permitidos em um identificador (letras, números, sublinhado)
        • punct -- pontuação
        • cntrl -- caracteres de controle
        • graph -- caracteres "visíveis"
        • print -- caracteres visíveis mais espaço
      • elas podem ser combinadas:
        • a classe [[:alpha:]] é composta de [[:lower:][:upper:]]
        • a classe [[:alnum:]] é composta de [[:alpha:][:digit:]]
        • a classe [[:word:]] é composta de [[:alnum:]_]
        • o sublinhado (octal 137) é tanto [[:word:]] quanto [[:punct:]]
        • a classe [[:graph:]] é composta de [[:alnum:][:punct:]]
        • a classe [[:print:]] é composta de [[:graph:] ] -- apenas espaço (octal 040), e não qualquer outro espaço em branco.
Iterando sobre uma lista de arquivos

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

Padrões glob não são expressões regulares.

  • corresponder a exatamente um caractere
    • regex .
    • glob ?
  • corresponder a zero ou mais caracteres
    • regex .*
    • glob *
      • o padrão glob .* significa "corresponder a um nome de arquivo em que o primeiro caractere é um ponto literal seguido de zero ou mais de qualquer caractere".
  • corresponder a um ou mais caracteres
    • regex .+
    • glob ?*
  • conjuntos de caracteres de regex e de glob funcionam da mesma forma.
Advanced

O Bash oferece padrões estendidos que são mais próximos de expressões regulares.

Aspas e expansão de nomes de arquivos

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:]

Caution

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.

Desativar a expansão de nomes de arquivos

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
Controlar a expansão de nomes de arquivos

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úsculas
  • shopt -s extglob -- ativa os padrões estendidos
  • shopt -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.
Editar via GitHub O link abre em uma nova janela ou aba