você está aqui: Home  → Arquivo de Mensagens

IFS - Inter Field Separator

Colaboração: Julio Cezar Neves

Data de Publicação: 09 de outubro de 2017

O shell tem uma variável interna chamada IFS - Inter Field Separator (será Tabajara? :) - cujo valor default podemos obter da seguinte forma:

$ echo "$IFS" | od -h
0000000 0920 0a0a
0000004

O programa od com a opção -h foi usado para gerar um dump hexadecimal da variável. E lá podemos ver:

Valor Hexadecimal Significado
09 <TAB>
20 Espaço
0a <ENTER>

Ou seja os separadores entre campos (tradução livre de IFS) default são o <TAB>, o espaço em branco e o <ENTER>. O IFS é usado em diversas instruções, mas seu uso é muito comum em par com o for e/ou com o read. Vejamos um exemplo semelhante ao dado no Cantinho do Shell de 15/12/2006, que me inspirou a escrever este artigo.

$ cat script1.sh
#!/bin/sh
while read linha
do
    awk -F: '{print $1}' /etc/passwd > /dev/null
done < /etc/passwd

Como podemos ver este script não faz nada (sua única saída foi enviada para /dev/null para não deturpar os tempos de execução), mas vamos usá-lo para avaliação dos tempos de execução.

Este script foi alterado, trocando o awk pelo cut, e ficando com a seguinte cara:

$ cat script2.sh
#!/bin/sh
while read linha
do
     echo $linha | cut -f1 -d: > /dev/null
done < /etc/passwd

Mais uma outra alteração, usando 99% de intrínsecos do Shell (exceto o comando echo) desta vez tomando partido do IFS:

$ cat script3.sh
#!/bin/sh
IFS=:
while read user lixo
do
     echo $lixo > /dev/null
done < /etc/passwd


Neste último exemplo, transformamos o separador padrão em dois-pontos (:) e usamos sua propriedade em conjunto com o read, isto é, o primeiro campo veio para a variável user e o resto para a variável lixo.

Em seguida, fiz um script usando Shell quase puro (novamente o echo foi o vilão). Repare a construção ${linha%%:*}, que é um intrínseco (built-in) do Shell que serve para excluir da variável linha o maior casamento com o padrão especificado (:* - que significa "de dois-pontos em diante"), ou seja, excluiu de linha tudo a partir do último dois-pontos, contado da direita para a esquerda.

$ cat script4.sh
#!/bin/sh
while read linha
do
     echo ${linha%%:*} > /dev/null
done < /etc/passwd

Para finalizar, adaptei o script escrito pelo incansável Rubens Queiroz que, exceto pelo awk, é Shell puro.

$ cat script5.sh
#!/bin/sh
for user in `awk -F: '{print $1}' /etc/passwd`
do
    echo $user > /dev/null
done

Agora, o mais importante: reparem os tempos da execução de cada um deles:

$ time script1.sh

real    0m0.123s
user    0m0.032s
sys     0m0.032s

$ time script2.sh

real    0m0.297s
user    0m0.152s
sys     0m0.084s

$ time script3.sh

real    0m0.012s
user    0m0.004s
sys     0m0.004s

$ time ./script4.sh

real    0m0.012s
user    0m0.004s
sys     0m0.008s

$ time ./script5.sh

real    0m0.014s
user    0m0.012s
sys     0m0.004s

Reparem que estas diferenças de tempo foram obtidas para um arquivo com somente 29 linhas. Veja:

$ wc -l /etc/passwd
29 /etc/passwd

Um outro uso interessante do IFS é o que vemos a seguir, primeiramente usando o IFS default que como vimos é <TAB>, Espaço e <ENTER>:

$ Frutas="Pera Uva Maçã"
$ set - $Frutas
$ echo $1
Pera

$ echo $3
Maçã

Agora, vamos alterar o IFS para fazer o mesmo com uma variável qualquer, e para tal vamos continuar usando o famigerado /etc/passwd:

$ Root=$(head -1 /etc/passwd)
$ echo $Root
root:x:0:0:root:/root:/bin/bash
$ oIFS="$IFS"
$ IFS=:
$ set - $Root
$ echo $1
root 
$ echo $7
/bin/bash 
$ IFS="$oIFS"

Senhoras e Senhores, neste artigo pretendi mostrar duas coisas:

  • O uso do IFS que, infelizmente para nós, é uma variável pouco conhecida do Shell e
  • Que quanto mais intrínsecos do Shell usamos, mais veloz e performático fica o script.

Sobre o autor

Júlio Cézar Neves

O 4º UNIX do mundo nasceu na Cidade Maravilhosa, mais precisamente na Cobra Computadores, onde à época trabalhava o Julio. Foi paixão à 1ª vista! Desde então, (1980) atua nessa área como especialista em Sistemas Operacionais e linguagens de programação. E foi por essa afinidade que quando surgiu o Linux foi um dos primeiros a estudá-lo com profundidade e adotá-lo como Sistema Operacional e filosofia de vida. É autor dos livros Programação Shell Linux, 11ª edição e Bombando o Shell.


Veja a relação completa dos artigos de Julio Cezar Neves

 

 

Opinião dos Leitores

julio Neves
10 Out 2017, 17:45
Caro Celio,
Nem há necessidade do hífen isso é cacoete meu desde os tempos do bourne shell primitivo.

A sintaxe do builtin set é a seguinte:

set [OPTs] [ARGs]

após tratar as opções OPTs, qualquer argumento ARG remanescente será tratado com um parâmetro posicional.

No man está escrito: "When options are specified, they set or unset shell attributes. Any arguments remaining after option processing are treated as values for the positional parameters and are assigned, in order, to $1, $2, ... $n."

Infelizmente me esqueci de dizer uma coisa importante: alguns cmds permitem que só altere o valor de uma variável do sistema somente durante a sua execução. Veja isso:

$ echo $LANG
pt_BR.UTF-8
$ date # Saída em pt_BR
Ter Out 10 17:35:45 BRT 2017
$ LANG=en_En date # saída será em en_EN (inglês)
Tue Oct 10 17:36:01 BRT 2017
$ echo $LANG # Mas a variável não foi alterada
pt_BR.UTF-8

O mesmo se pode fazer com o IFS e o cmd read. Veja outra forma de fazer o while que fizemos acima:

while IFS=: read user lixo
do
echo $user > /dev/null
done < /etc/passwd

Fazendo desta forma não é preciso alterar o IFS e depois "desalterá-lo" (ARGHHH)

Celio
10 Out 2017, 16:50
Muito bom amigo, interessante como pequenas mudanças causam grandes efeitos... Fiquei com uma dúvida que nem pesquisando encontrei, quando você usa o set - $var porque os valores vão para as variáveis $1 $2 $...?
*Nome:
Email:
Me notifique sobre novos comentários nessa página
Oculte meu email
*Texto:
 
  Para publicar seu comentário, digite o código contido na imagem acima
 


Powered by Scriptsmill Comments Script