Bash Script – Variável de ambiente IFS
A variável de ambiente IFS é uma variável importante do BASH, é usada para informar o BASH qual separador usar para separar palavras em uma linha de texto por exemplo, ou em argumentos passados para scripts.
Neste artigo pretendo mostrar como podemos usa la e modifica-la a nosso gosto.
Modificar esta variável em determinadas situações tem vantagens, em outras não no entanto temos que perceber como funciona para podermos tomar a decisão, se devemos ou não modifica-la.
Todos os scripts de exemplo, encontram se no final do artigo, podem ser descarregados.
Valor por defeito
O valor por defeito desta variável é <space><tab><newline>.
Isto é o normal, quando lançamos comandos com argumentos separamos os argumentos com espaço. Quando criamos algum texto usamos espaço para separar a palavras e tabs para indentar algum texto e enter(newline) para mudar de linha. Por estas razões e talvez outras foram escolhidos estes separadores por defeito.
Verificar o valor por defeito
Para podermos verificar o valor por defeito, podemos correr o script “IFS_ex_1.sh”:
#!/bin/bash
#IFS_ex_1.sh
# Verificar o valor por defeito da variável IFS
echo "Verificar o valor por defeito da variável IFS"
printf "O valor atual é %q" "$IFS"
echo
miguel@terra:~/bash$ ./IFS_ex_1.sh
Verificar o valor por defeito da variável IFS
O valor atual é $' \t\n'
Visto que os caracteres usados não são visíveis temos de escapa-los para vermos. Isto é feito com o comando printf.
Modificando a variável IFS
Existe em determinadas situações a necessidade de modificar a variável IFS para que os nosso scripts se tornem mais simples. E consigam adaptar se melhor com os dados que estamos a lidar.
Os que tenham interesse, podem ver aqui um caso real onde modifico bastante a variável IFS para se adaptar aos dados.
Imaginemos uma lista com nome e email de pessoas da seguinte forma:
Miguel Caldeira miguel@exemplo.com
Maria Gertrudes mariag@exemplo.com
Rita Maria Gertrudes rmariag@exemplo.com
Claro que poderíamos processar estes dados sem modificar a variável IFS.
Neste caso por exemplo sabemos de certeza que o ultimo argumento é o email da pessoa em questão e que todos os outros são o seu nome. Simples. Apesar de nem todos terem o mesmo numero de nomes. No entanto como só temos o nome e email e sabemos que o email não tem espaços e é sempre escrito em último é fácil processar estes dados.
Podemos processar estes dados com o seguinte script:
#!/bin/bash
#IFS_ex_2.sh
# Esta variável é uma copia dos parâmetros passados por argumento
dados=$@
# Remover o nome
# Apaga tudo a partir do inicio até encontrar o ultimo espaço " "
echo "Email: ${dados##* }"
# Remover o email
# Apaga tudo a partir do fim até encontrar o " "
echo "Nome: ${dados% *}"
miguel@terra:~/bash$ ./IFS_ex_2.sh Miguel Caldeira miguel@exemplo.com
Email: miguel@exemplo.com
Nome: Miguel Caldeira
Apesar de nesta situação ser fácil processar estes dados, tudo ficaria mais complicado caso se tenha mais de que um dado que contenha espaços.
Imaginemos a seguinte lista que contem nome, morada e email:
Miguel Caldeira rua sésamo miguel@exemplo.com
Maria Gertrudes rua sésamo mariag@exemplo.com
Rita Maria Gertrudes rua sésamo rmariag@exemplo.com
Claro que nós humanos se lermos com atenção conseguimos separar o nome a morada e o email. No entanto para o BASH todos os espaços são iguais. Seria bastante difícil separar estes dados usando como separador o espaço ” “.
Nesta situação torna-se muito mais fácil atribuir um separador diferente para separar os dados. No entanto este separador não pode aparecer nos dados.
Desta forma é possível separar os dados de cada pessoa em três partes independentemente do numero de palavras que cada parte tenha. Neste exemplo vou usar o sinal (+) que não faz parte de nomes nem moradas nem emails.
Da seguinte forma:
Miguel Caldeira+Rua Sésamo+miguel@exemplo.com
Maria Gertrudes+Rua Sésamo+mariag@exemplo.com
Rita Maria Gertrudes+Rua Sésamo+rmariag@exemplo.com
Desta forma apesar dos nomes conterem espaços bem como a morada não existe problema pois o que os separa é o sinal “+”.
No seguinte script BASH a variável IFS é configurada para usar o sinal “+” como separador:
#!/bin/bash
#IFS_ex_3.sh
# Configurar o BASH para usar o sinal '+' como separador
IFS='+'
# Criar o array 'pessoadados' com os valores passados por argumento
# para o script separando o valores por '+'
read -a pessoadados <<< "$@"
# Tendo o array com os dados podemos usar a gosto
# no decorrer do script.
# O nome da pessoa passado por argumento
echo "Nome: ${pessoadados[0]}"
# Morada da pessoa passado por argumento
echo "Morada: ${pessoadados[1]}"
# O email da pessoa passado por argumento
echo "Email: ${pessoadados[2]}"
Quando corremos o script anterior com os dados separados com o sinal ‘+’ temos o seguinte output.
miguel@terra:~/bash$ ./IFS_ex_3.sh Rita Maria Gertrudes+Rua Sésamo+rmariag@exemplo.com
Nome: Rita Maria Gertrudes
Morada: Rua Sésamo
Email: rmariag@exemplo.com
Nos exemplos acima os dados de cada pessoa são inseridos como argumento para o script, no entanto os dados podem ser obtidos de ficheiros ou até mesmo através dos dados resultantes de o processamento de um comando.
Os argumentos e a variável IFS
Quando trabalhamos com argumentos, usamos as variáveis especiais “$@”, “$*” bem como “$0”, “$1”, “$n”, para ter acesso aos valores passados por argumento.
No entanto apenas as variáveis “$@” e “$*” são influenciadas pela variável de ambiente “IFS”.
Reset a variável IFS
Normalmente modificamos a variável IFS para obter e organizar os dados em strings ou arrays, depois podemos fazer reset a esta variável. A forma que temos para fazer isto é criando uma copia da variável IFS logo no inicio do script para mais tarde repor o seu valor para usar no resto do script.
O seguinte script exemplifica isto:
#!/bin/bash
#IFS_ex_4.sh
# Guardar o valor por defeito da variável IFS.
OLDIFS=$IFS
# Configurar o BASH para usar o sinal '+' como separador
# desta forma podemos separar os argumentos com o sinal '+'
IFS='+'
# Criar u array 'pessoadados' com os valores passados por argumento para o script
# separando o valores por '+'
read -a pessoadados <<< "$@"
# Tendo o array com os dados podemos usar a gosto no decorrer do script.
# O nome da pessoa passado por argumento
echo "Nome: ${pessoadados[0]}"
# Morada da pessoa passado por argumento
echo "Morada: ${pessoadados[1]}"
# O email da pessoa passado por argumento
echo "Email: ${pessoadados[2]}"
# Como já temos os nossos dados organizados podemos repor o valor da variável IFS
# com o seu valor por defeito para usar no resto do script.
IFS=$OLDIFS
Conclusão
Por vezes é importante usar a variável IFS para ler dados formatados, pois de outra forma complicaria todo o script para fazer algo bem simples.
Temos de ter o cuidado em repor o valor da variável IFS, se pretendermos ler dados separados por espaço no decorrer do script.
Espero que tenha sido claro e que qualquer um possa perceber este artigo e que possa tirar proveito dele.
Qualquer questão comentem ou usem o grupo Bash Script.
4 comments
Joaquim Lobo Neto
Olá José Miguel, felizmente um mecanismo de pesquisa trouxe-me até aqui, sorte a minha pois os seus artigos sobre bash scripts são muito bons.
Comecei aprendendo do zero lendo-os e já aprendi muito, mas tenho uma dúvida; não encontrei nos artigos um comando: read -a pessoadados <<< "$@" que você usou no exemplo #IFS_ex_3.sh.
Ficarei muito agradecido se você puder comentar sobre o "read -a" e também "<<<".
Desejo a você muita prosperidade e o meu sincero incentivo na continuação dos artigos de bash script.
José Miguel Silva Caldeira
Olá Joaquim, desde já obrigado!
Eu tento não deixar nada para trás, mas é difícil.
Irei escrever sobre o comando “read” em um artigo. Em outro sobre redirecionamento pretendo abordar estes sinais "<<<".
Joaquim Lobo Neto
José Miguel, muitíssimo obrigado pela atenção e não se sinta culpado pois o seu trabalho é ótimo.
Vou aproveitar a oportunidade e pedir mais uma orientação!
No script IFS_ex_3.sh se eu acrescentasse no final dele as seguintes linhas:
# Número de argumentos
echo "Foram passados ao script $# argumentos"
# Argumentos
echo "O argumento um é : $1"
echo "O argumento dois é : $2"
echo "O argumento três é : $3"
echo "O argumento quatro é : $4"
que você usou no script var_especiais_ex_1.sh do artigo de variáveis especiais, ao corrermos o script com os dados separados com o sinal ‘+’ temos o seguinte output:
miguel@terra:~/bash$ ./IFS_ex_3.sh Rita Maria Gertrudes+Rua Sésamo+rmariag@exemplo.com
Nome: Rita Maria Gertrudes
Morada: Rua Sésamo
Email: rmaria@exemplo.com
Foram passados ao script 4 argumentos
O argumento um é : Rita
O argumento dois é : Maria
O argumento três é : Gertrudes+Rua
O argumento quatro é : Sésamo+rmaria@exemplo.com
Percebi que o sinal '+' não afetou as variáveis $1, $2, $3 e $4, então eu pergunto: Há alguma maneira de aplicar a estas variáveis o mesmo comportamento dado a variável $IFS?
José Miguel Silva Caldeira
Olá Joaquim, disponha!
Se possível gostava que se regista-se no site e usa-se o grupo https://www.ncdc.pt/groups/bash-script/.
Não é obrigatório, mas seria uma melhor forma de separar assuntos. E permitir que outros tenham acesso de forma mais simples ás mesmas perguntas.
A variável IFS apenas afeta a variável especial '$@' e '$*'.
Se pretender usar um argumento levando em conta a ação da variável IFS, no script "./IFS_ex_3.sh" utilize o comando "read".
Ficaria assim o script:
#!/bin/bash
#IFS_ex_3.sh
# Configurar o BASH para usar o sinal '+' como separador
IFS='+'
# Criar u array 'pessoadados' com os valores passados por argumento para o script
# separando o valores por '+'
read -a pessoadados <<< "$@"
# Tendo o array com os dados podemos usar a gosto no decorrer do script.
# O nome da pessoa passado por argumento
echo "Nome: ${pessoadados[0]}"
# Morada da pessoa passado por argumento
echo "Morada: ${pessoadados[1]}"
# O email da pessoa passado por argumento
echo "Email: ${pessoadados[2]}"
# Número de argumentos
echo "Foram passados ao script ${#pessoadados[@]} argumentos"
# Argumentos
echo "O argumento um é : ${pessoadados[0]}"
echo "O argumento dois é : ${pessoadados[1]}"
echo "O argumento três é : ${pessoadados[2]}"