You are here:
Wiki-SL
>
TWikiBar Web
>
TWikiBarBirinaite
(01 Aug 2009,
BrSalgado
)
(raw view)
E
dit
A
ttach
---+!! Tira Gosto ---+!! Shell em pedaços pequenos e gostosos ------ ---+++!! *Em construção para sempre* ------ Esta página, apesar de estar no escopo do Papo de Botequim, nunca foi publicada na _Linux Magazine_. Trata-se de artigos que escrevi para outras mídias, dicas úteis que li pela internet afora (e neste caso com os devidos créditos), contribuições deste pessoal do __Software Livre__, maravilhoso e sempre pronto a ajudar e da imperdível <a href="http://br.groups.yahoo.com/group/shell-script/" target="_blank">Lista de Shell Script</a> ------ %TOC% ------ ---++ Passando parâmetros com xargs Existe um comando, cuja função primordial é construir listas de parâmetros e passá-la para a execução de outros programas ou instruções. Este comando é o =xargs= e deve ser usado da seguinte maneira: <verbatim> xargs [comando [argumento inicial]] </verbatim> =xargs= combina o argumento inicial com os argumentos recebidos da entrada padrão, de forma a executar o comando especificado uma ou mais vezes. <kbd>Exemplo:</kbd> Vamos procurar em todos os arquivos abaixo de um determinado diretório uma cadeia de caracteres usando o comando =find= com a opção =-type f= para pesquisar somente os arquivos normais, desprezando diretórios, arquivos especiais, arquivos de ligações, etc, e vamos torná-la mais genérica recebendo o nome do diretório inicial e a cadeia a ser pesquisada como parâmetros. Para isso fazemos: %TERMINAL_INI% $ cat grepr%OUT_INI% # # Grep recursivo # Pesquisa a cadeia de caracteres definida em $2 a partir do diretorio $1 # find $1 -type f -print|xargs grep -l "$2"%OUT_FIM% %TERMINAL_FIM% Na execução deste script procuramos, a partir do diretório definido na variável =$1=, todos os arquivos que continham a cadeia definida na variável =$2=. Exatamente a mesma coisa poderia ser feito se a linha do programa fosse a seguinte: <verbatim> find $1 -type f -exec grep -l "$2" {} \; </verbatim> O primeiro processo tem duas grandes desvantagens sobre o anterior: * A primeira é bastante visível: o tempo de execução deste método é muito superior ao daquele, isso porque o =grep= será feito em cada arquivo que lhe for passado pelo =find=, um-a-um, ao passo que com o =xargs=, será passada toda, ou na pior das hipóteses, a maior parte possível, da lista de arquivos gerada pelo =find=; * Dependendo da quantidade de arquivos encontrados que atendem ao =find=, poderemos ganhar aquela famosa e fatídica mensagem de erro =Too many arguments= indicando um estouro da pilha de execução do =grep=. Como foi dito no item anterior, se usarmos o =xargs= ele passará para o =grep= a maior quantidade de parâmetros possível, suficiente para não causar este erro, e caso necessário executará o =grep= mais de uma vez. %ATENCAO_INI% Aê pessoal do linux que usa o =ls= colorido que nem porta de tinturaria: nos exemplos a seguir que envolvem esta instrução, você devem usar a opção =--color=none=, senão existem grandes chances dos resultados não ocorrerem como o esperado. %ATENCAO_FIM% Vamos agora analisar um exemplo que é mais ou menos o inverso deste que acabamos de ver. Desta vez, vamos fazer um _script_ para remover todos os arquivos do diretório corrente, pertencentes a um determinado usuário. A primeira idéia que surge é, como no caso anterior, usar um comando =find=, da seguinte maneira: <verbatim> find . -user cara -exec rm -f {} \; </verbatim> Quase estaria certo, o problema é que desta forma você removeria não só os arquivos do =cara= no diretório corrente, mas também de todos os outros subdiretórios "pendurados" neste. Vejamos então como fazer: <verbatim> ls -l | grep " cara " | cut -c55- | xargs rm </verbatim> Desta forma, o =grep= selecionou os arquivos que continham a cadeia =cara= no diretório corrente listado pelo =ls -l=. O comando =cut= pegou somente o nome dos arquivos, passando-os para a remoção pelo =rm= usando o comando =xargs= como ponte O xargs é também uma excelente ferramenta de criação de _one-liners_ (_scripts_ de somente uma linha). Veja este para listar todos os donos de arquivos (inclusive seus _links_) "pendurados" no diretório =/bin= e seus subdiretórios. %TERMINAL_INI% $ find /bin -type f -follow | \ xargs ls -al | tr -s ' ' | cut -f3 -d' ' | sort -u %TERMINAL_FIM% Muitas vezes o =/bin= é um _link_ (se não me engano, no Solaris o é) e a opção =-follows= obriga o =find= a seguir o _link_. O comando =xargs= alimenta o =ls -al= e a seqüência de comandos seguinte é para pegar somente o 3º campo (dono) e classificá-lo devolvendo somente uma vez cada dono (opção =-u= do comando =sort=, que equivale ao comando =uniq=). ---+++ Opções do xargs Você pode usar as opções do =xargs= para construir comandos extremamente poderosos. ---++++ Opção -i Para exemplificar isso e começar a entender as principais opções desta instrução, vamos supor que temos que remover todos as arquivos com extensão =.txt= sob o diretório corrente e apresentar os seus nomes na tela. Veja o que podemos fazer: %TERMINAL_INI% $ find . -type f -name "*.txt" | \ xargs -i bash -c "echo removendo {}; rm {}" %TERMINAL_FIM% A opção =-i= do =xargs= troca pares de chaves =({})= pela cadeia que está recebendo pelo pipe =(|)=. Então neste caso as chaves =({})= serão trocadas pelos nomes dos arquivos que satifaçam ao comando =find=. ---++++ Opção -n Olha só a brincadeira que vamos fazer com o =xargs=: %TERMINAL_INI% $ ls | xargs echo > arq.ls $ cat arq.ls%OUT_INI% arq.ls arq1 arq2 arq3%OUT_FIM% $ cat arq.ls | xargs -n1%OUT_INI% arq.ls arq1 arq2 arq3%OUT_FIM% %TERMINAL_FIM% Quando mandamos a saída do =ls= para o arquivo usando o =xargs=, comprovamos o que foi dito anteriormente, isto é, o =xargs= manda tudo que é possível (o suficiente para não gerar um estouro de pilha) de uma só vez. Em seguida, usamos a opção =-n 1= para listar um por vez. Só para dar certeza veja o exemplo a seguir, quando listaremos dois em cada linha: %TERMINAL_INI% $ cat arq.ls | xargs -n 2%OUT_INI% arq.ls arq1 arq2 arq3%OUT_FIM% %TERMINAL_FIM% Mas a linha acima poderia (e deveria) ser escrita sem o uso de _pipe_ =(|)=, da seguinte forma: %TERMINAL_INI% $ xargs -n 2 < arq.ls %TERMINAL_FIM% ---++++ Opção -p Outra opção legal do =xargs= é a =-p=, na qual o sistema pergunta se você realmente deseja executar o comando. Digamos que em um diretório você tenha arquivos com a extensão =.bug= e =.ok=, os =.bug= estão com problemas que após corrigidos são salvos como =.ok=. Dá uma olhadinha na listagem deste diretório: %TERMINAL_INI% $ ls dir%OUT_INI% arq1.bug arq1.ok arq2.bug arq2.ok ... arq9.bug arq9.ok%OUT_FIM% %TERMINAL_FIM% Para comparar os arquivos bons com os defeituosos, fazemos: %TERMINAL_INI% $ ls | xargs -p -n2 diff -c%OUT_INI% diff -c arq1.bug arq1.ok ?...y .... diff -c arq9.bug arq9.ok ?...y%OUT_FIM% %TERMINAL_FIM% ---++++ Opção -t Para finalizar, o =xargs= também tem a opção =-t=, onde vai mostrando as instruções que montou antes de executá-las. Gosto muito desta opção para ajudar a depurar o comando que foi montado. ---++++ Resumo Então podemos resumir o comando de acordo com a tabela a seguir: <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *Opção* | *Ação* | | =-i= | Substitui o par de chaves =({})= pelas cadeias recebidas | | =-nNum= | Manda o máximo de parâmetros recebidos, até o máximo de Num para o comando a ser executado | | =-lNum= | Manda o máximo de linhas recebidas, até o máximo de Num para o comando a ser executado | | =-p= | Mostra a linha de comando montada e pergunta se deseja executá-la | | =-t= | Mostra a linha de comando montada antes de executá-la | </center> ---++ Here Strings %BIRINAITE_INI% Acabei de dar um treinamento no Instituto de Pesquisas Eldorado, onde peguei uma turma de 12 alunos de altíssima qualidade. Devido ao preparo do pessoal, o nível do curso foi tão bom que cheguei a abordar alguns conceitos pouco usados como o redirecionamento por _here strings_. Então pensei: se é uma coisa tão útil, porque não mostrá-la a todos? Ai vai ... %BIRINAITE_FIM% Primeiro um programador com complexo de inferioridade criou o redirecionamento de entrada e representou-o com um sinal de menor =(<)= para representar seus sentimento. Em seguida, outro sentindo-se pior ainda, criou o _here document_ representando-o por dois sinais de menor =(<<)= porque sua fossa era maior. O terceiro, pensou: "estes dois não sabem o que é estar por baixo"... Então criou o _here strings_ representado por três sinais de menor =(<<<)=. Brincadeiras a parte, o _here strings_ é utilíssimo e, não sei porque, é um perfeito desconhecido. Na pouquíssima literatura que há sobre o tema, nota-se que o _here strings_ é freqüentemente citado como uma variante do _here document_, teoria com a qual discordo pois sua aplicabilidade é totalmente diferente daquela. Sua sintaxe é simples: <verbatim> $ comando <<< $cadeia </verbatim> Onde =cadeia= é expandida e alimenta a entrada primária (_stdin_) de =comando=. Como sempre, vamos direto aos exemplos dos dois usos mais comuns para que vocês próprios tirem suas conclusões. * *Uso #1*. Substituindo a famigerada construção =echo "cadeia" | comando=, que força um _fork_, criando um _subshell_ e onerando o tempo de execução. <kbd>Exemplos:</kbd> %TERMINAL_INI% $ a="1 2 3" $ cut -f 2 -d ' ' <<< $a # Normalmente faz-se: echo $a | cut -f 2 -d ' '%OUT_INI% 2%OUT_FIM% $ echo $NomeArq%OUT_INI% Meus Documentos%OUT_FIM% # Arrrghhh! $ tr "A-Z " "a-z_" <<< $NomeArq # Substituindo o echo $NomeArq | tr "A-Z " "a-z_"%OUT_INI% meus_documentos%OUT_FIM% $ bc <<<"3 * 2"%OUT_INI% 6%OUT_FIM% $ bc <<<"scale = 4; 22 / 7"%OUT_INI% 3.1428%OUT_FIM% %TERMINAL_FIM% Para mostrar a melhoria no desempenho, vamos fazer um loop de 500 vezes usando o exemplo dados para o comando =tr=: Veja agora esta seqüência de comandos com medidas de tempo: %TERMINAL_INI% $ time for ((i=1; i<= 500; i++)); { tr "A-Z " "a-z_" <<< $NomeArq >/dev/null; } %OUT_INI% real 0m3.508s user 0m2.400s sys 0m1.012s%OUT_FIM% $ time for ((i=1; i<= 500; i++)); { echo $NomeArq | tr "A-Z " "a-z_" >/dev/null; } %OUT_INI% real 0m4.144s user 0m2.684s sys 0m1.392s%OUT_FIM% %TERMINAL_FIM% Veja agora esta seqüência de comandos com medidas de tempo: %TERMINAL_INI% $ time for ((i=1;i<=100;i++)); { who | cat > /dev/null; } %OUT_INI% real 0m1.435s user 0m1.000s sys 0m0.380s%OUT_FIM% $ time for ((i=1;i<=100;i++)); { cat <(who) > /dev/null; } %OUT_INI% real 0m1.552s user 0m1.052s sys 0m0.448s%OUT_FIM% $ time for ((i=1;i<=100;i++)); { cat <<< $(who) > /dev/null; } %OUT_INI% real 0m1.514s user 0m1.056s sys 0m0.412s%OUT_FIM% %TERMINAL_FIM% Observando este quadro você verá que no primeiro usamos a forma convencional, no segundo usamos um _named pipe_ temporário para executar uma substituição de processos e no terceiro usamos _here strings_. Notará também que ao contrário do exemplo anterior, aqui o uso de _here strings_ não foi o mais veloz. Mas repare bem que neste último caso o comando =who= está sendo executado em um _subshell_ e isso onerou o processo como um todo. Vejamos uma forma rápida de inserir uma linha como cabeçalho de um arquivo: %TERMINAL_INI% $ cat num%OUT_INI% 1 2 3 4 5 6 7 8 9 10%OUT_FIM% $ cat - num <<< "Impares Pares"%OUT_INI% Impares Pares 1 2 3 4 5 6 7 8 9 10%OUT_FIM% %TERMINAL_FIM% * *Uso #2*. Outra forma legal de usar o _here strings_ é casando-o com um comando =read=, não perdendo de vista o que aprendemos sobre =IFS= (<a href="http://twiki.softwarelivre.org/bin/view/TWikiBar/TWikiBarPapo005#Primeira_sintaxe_do_comando_for" target="_blank">veja aqui, na explicação do comando for</a>). O comando =cat= com as opções =-vet= mostra o =<ENTER>= como =$=, o =<TAB>= como =^I= e os outros caracteres de controle com a notação =^L= onde =L= é uma letra qualquer. Vejamos então o conteúdo de uma variável e depois vamos ler cada um de seus campos: <kbd>Exemplos:</kbd> %TERMINAL_INI% $ echo "$Linha"%OUT_INI% Leonardo Mello (21)3313-1329%OUT_FIM% $ cat -vet <<< "$Linha"%OUT_INI% Leonardo Mello^I(21)3313-1329$%OUT_FIM% # Os separadores sao branco e <TAB> (^I) $ read Nom SNom Tel <<< "$Linha" $ echo "${Nom}_$S{Nom}_$Tel" # Vamos ver se ele leu cada um dos campos%OUT_INI% Leonardo_Mello_(21)3313-1329%OUT_FIM% # Leu porque os separadores casavam com o IFS %TERMINAL_FIM% Também podemos ler direto para um vetor (_array_) veja: %TERMINAL_INI% $ echo $Frutas%OUT_INI% Pera:Uva:Maçã%OUT_FIM% $ IFS=: $ echo $Frutas%OUT_INI% Pera Uva Maçã%OUT_FIM% # Sem as aspas o shell mostra o IFS como branco $ echo "$Frutas"%OUT_INI% Pera:Uva:Maçã%OUT_FIM% # Ahhh, agora sim! $ read -a aFrutas <<< "$Frutas" # A opção -a do read, lê para um vetor $ for i in 0 1 2 > do > echo ${aFrutas[$i]} # Imprimindo cada elemento do vetor > done%OUT_INI% Pera Uva Maçã%OUT_FIM% %TERMINAL_FIM% ---++ Rotatório Peczenyj %BIRINAITE_INI% Estava, como faço todo dia, dando um lida nos e-mails da <a href="http://br.groups.yahoo.com/group/shell-script/" target="_blank"> "Lista de Shell Script" </a>, quando vi uma "descoberta" totalmente inusitada do Tiago Barcellos Peczenyj. Quando resolvi montar esta coletânea de dicas, me lembrei disso e pedi-lhe para me encaminhar aquele e-mail novamente. O texto a a seguir é o e-mail que ele me mandou, só inseri o último exemplo e tirei as abreviaturas. %BIRINAITE_FIM% Julio descobri uma forma para o _Shell_ criar combinaçãoes fazendo rotação com os elementos estipulados. Podemos gerar todos os binários de 0000 a 1111 da seguinte forma: %TERMINAL_INI% $ A={0,1} $ eval echo $A$A$A$A%OUT_INI% 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111%OUT_FIM% %TERMINAL_FIM% Uma aplicação pratica que vejo é para combinar valores diferentes sem ter que encadear _loops_ nem usar o =seq= %TERMINAL_INI% $ A={`seq -s , -f "_%g" 3`} $ eval echo -e $A$A$A |tr ' _' '\n ' | grep -vE '.+?(\b[0-9]+\b).+?\1'%OUT_INI% 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1%OUT_FIM% %TERMINAL_FIM% Neste caso eu combinei os números de 1 a 3 eliminando repetições com o =grep=. Usei um =tr= 'podre' para melhor tratar os dados, saltando linha. O =grep= é simples como você pode notar, eu vejo se uma determinada parte da combinação (=.+?(\b[0-9]+\b).+?=) existe em outra parte (=\1=), se existe eu não deixo imprimir por causa da opção =-v=, assim <verbatim> 1 1 2 1 2 1 1 1 1 </verbatim> não serão impressos. Agora vai o meu exemplo: o one-liner a seguir gerará todas as permissões possíveis (em octal) para o arquivo =arq= (o exemplo foi interrompido porque existem 512 combinações de permissões possíveis). %TERMINAL_INI% $ A={`seq -s , 0 7`} $ eval echo -e $A$A$A | tr ' ' '\n' | xargs -i bash -c "chmod {} arq; ls -l arq"%OUT_INI% <nop>---------- 1 julio julio 100 2006-11-27 11:50 arq <nop>---------x 1 julio julio 100 2006-11-27 11:50 arq <nop>--------w- 1 julio julio 100 2006-11-27 11:50 arq <nop>--------wx 1 julio julio 100 2006-11-27 11:50 arq <nop>-------r-- 1 julio julio 100 2006-11-27 11:50 arq <nop>-------r-x 1 julio julio 100 2006-11-27 11:50 arq <nop>-------rw- 1 julio julio 100 2006-11-27 11:50 arq <nop>-------rwx 1 julio julio 100 2006-11-27 11:50 arq . . . . . . . . . . . . . . . . . . <nop>-rwxrwxrw- 1 julio julio 100 2006-11-27 11:50 arq <nop>-rwxrwxrwx 1 julio julio 100 2006-11-27 11:50 arq%OUT_FIM% %TERMINAL_FIM% Vamos ver este exemplo passo-a-passo para entendê-lo: %TERMINAL_INI% $ echo $A%OUT_INI% {0,1,2,3,4,5,6,7}%OUT_FIM% $ eval echo -e $A$A$A%OUT_INI% 000 001 002 003 004 005 006 007 010 ... ... 767 770 771 772 773 774 775 776 777%OUT_FIM% $ eval echo -e $A$A$A | tr ' ' '\n'%OUT_INI% # O tr trocará cada espaco em branco por um <ENTER> 000 001 002 003 . . . 774 775 776 777%OUT_FIM% %TERMINAL_FIM% A seguir o =xargs= (<a href="https://twiki.softwarelivre.org/bin/view/TWikiBar/TWikiBarBirinaite#Passando_par%E2metros_com_xargs">clique para dicas do xargs</a>) executa o comando =bash -c= (que serve para executar uma linha de comandos) que por sua vez executa o =chmod= e o =ls -l= para mostrar que as permissões estão sendo alteradas. ---++ Aritmética em Shell Antigamente usávamos o comando <code>expr</code> para fazer operações aritméticas e muita gente ainda usa, pois é compatível com quaquer ambiente. <kbd>Exemplo:</kbd> %TERMINAL_INI% $ expr 7 \* 5 / 3 # 7 vezes 5 = 35 dividido por 3 = 11%OUT_INI% 14%OUT_FIM% %TERMINAL_FIM% Neste artigo porém, vamos ver outras formas não tanto conhecidas, porém mais simples de usar, mais elaboradas e com precisão maior. ---+++ O uso do bc Uma forma bacana de fazer cálculos em Shell – usada normalmente quando a expressão aritmética é mais complexa, ou quando é necessário trabalharmos com casas decimais – é usar a instrução calculadora do UNIX/LINUX. O <code>bc</code>. Veja como: <kbd>Exemplo:</kbd> %TERMINAL_INI% $ echo "(2 + 3) * 5" | bc # Parênteses usados para dar precedência%OUT_INI% 25%OUT_FIM% %TERMINAL_FIM% Para trabalhar com números reais (números não necessariamente inteiros), especifique a precisão (quantidade de decimais) com a opção <code>scale</code> do comando <code>bc</code>. Assim vejamos o penúltimo exemplo: %TERMINAL_INI% $ echo "scale=2; 7*5/3" | bc%OUT_INI% 11.66%OUT_FIM% %TERMINAL_FIM% Outros exemplos: %TERMINAL_INI% $ echo "scale=3; 33.333*3" | bc%OUT_INI% 99.999%OUT_FIM% $ num=5 $ echo "scale=2; ((3 + 2) * $num + 4) / 3" | bc%OUT_INI% 9.66%OUT_FIM% %TERMINAL_FIM% Obviamente todos os exemplos acima no caso de linux, poderiam (e deveriam) ser escritos usando _Here Strings_. Veja os últimos como ficariam: %TERMINAL_INI% $ bc <<< "scale=3; 33.333*3"%OUT_INI% 99.999%OUT_FIM% $ num=5 $ bc <<< "scale=2; ((3 + 2) * $num + 4) / 3"%OUT_INI% 9.66%OUT_FIM% %TERMINAL_FIM% Uma vez apareceu na lista (excelente por sinal) de Shell script no Yahoo (http://br.groups.yahoo.com/group/shell-script/) um cara com a seguinte dúvida: "eu tenho um arquivo cujos campos estão separados por <code><TAB></code> e o terceiro deles possui números. Como posso calcular a soma de todos os números desta coluna do arquivo?" Mandei a seguinte resposta: %TERMINAL_INI% $ echo $(cut -f3 num | tr '\n' +)0 | bc%OUT_INI% 20.1%OUT_FIM% %TERMINAL_FIM% Vamos por partes para entender melhor e primeiramente vamos ver como era o arquivo que fiz para teste: %TERMINAL_INI% $ cat num%OUT_INI% a b 3.2 a z 4.5 w e 9.6 q w 2.8%OUT_FIM% %TERMINAL_FIM% Como pode-se ver, está dentro do padrão do problema, onde eu tenho como terceiro campo números reais. Vamos ver o que faria a primeira parte da linha de comandos, onde eu transformo os caracteres <code><ENTER></code> (_new-line_) em um sinal de mais (<code>+</code>): %TERMINAL_INI% $ cut -f3 num | tr '\n' +%OUT_INI% 3.2+4.5+9.6+2.8+%OUT_FIM% %TERMINAL_FIM% Se eu mandasse desse jeito para o =bc=, ele me devolveria um erro por causa daquele sinal de mais (<code>+</code>) solto no final do texto. A minha saída foi colocar um zero no final, pois somando zero o resultado não se alterará. Vamos ver então como ficou: %TERMINAL_INI% $ echo $(cut -f3 num | tr -s '\n' +)0%OUT_INI% 3.2+4.5+9.6+2.8+0%OUT_FIM% %TERMINAL_FIM% Isso é o que se costuma chamar _one-liner_, isto é, códigos que seriam complicados em outras linguagens (normalmente seria necessário criar contadores e fazer um _loop_ de leitura somando o terceiro campo ao contador) e em Shell são escritos em uma única linha. Há também gente que chama isso de método *KISS*, que é o acrônimo de _Keep It Simple Stupid_. :-) Mas o potencial de uso desta calculadora não se encerra aí, existem diversas facilidades por ela propiciadas. Veja só este exemplo: %TERMINAL_INI% $ echo "obase=16; 11579594" | bc%OUT_INI% B0B0CA%OUT_FIM% $ echo "ibase=16; B0B0CA" | bc # B, zero, B, zero, C, e A%OUT_INI% 11579594%OUT_FIM% %TERMINAL_FIM% Nestes exemplos vimos como fazer mudanças de base de numeração com o uso do =bc=. Na primeira explicitamos a base de saída (=obase=) como =16= (hexadecimal) e na segunda, dissemos que a base da entrada (=ibase=) era =10= (decimal). ---+++ Outras formas de trabalhar com inteiros Outra forma muito legal de fazer cálculos é usar a notação =$((exp aritmética))=. É bom ficar atento, porém, ao fato desta sintaxe não ser universalizada. O Bourne Shell (=sh=), por exemplo, não a reconhece. <kbd>Exemplo:</kbd> Usando o mesmo exemplo que já havíamos usado: %TERMINAL_INI% $ echo $(((2+3)*5)) # Os parênteses mais internos priorizaram o 2+3%OUT_INI% 25%OUT_FIM% %TERMINAL_FIM% Agora olha só esta maluquice: %TERMINAL_INI% $ tres=3 $ echo $(((2+tres)*5)) # Variável tres não precedida pelo $%OUT_INI% 25%OUT_FIM% $ echo $(((2+$tres)*5)) # Variável tres precedida pelo $%OUT_INI% 25%OUT_FIM% %TERMINAL_FIM% Ué!! Não é o cifrão (=$=) precedente que caracteriza uma variável? Sim, porém em todos os sabores UNIX que testei, sob bash ou ksh, ambas as formas de construção produzem uma boa aritmética. Preste a atenção nesta seqüência: %TERMINAL_INI% $ unset i # $i mooorreu! $ echo $((i++))%OUT_INI% 0%OUT_FIM% $ echo $i%OUT_INI% 1%OUT_FIM% $ echo $((++i))%OUT_INI% 2%OUT_FIM% $ echo $i%OUT_INI% 2%OUT_FIM% %TERMINAL_FIM% Repare que apesar da variável não estar definida, pois foi feito um =unset= nela, nenhum dos comandos acusou erro, porque, como estamos usando construções aritméticas, sempre que uma variável não existe, é inicializada com zero (=0=). Repare que o =i++= produziu zero (=0=). Isto ocorre porque este tipo de construção chama-se pós-incrementação, isto é, primeiramente o comando é executado e só então a variável é incrementada. No caso do =++i=, foi feita uma pré-incrementação: primeiro incrementou e somente após o comando foi executado. Também são válidos: %TERMINAL_INI% $ echo $((i+=3))%OUT_INI% 5%OUT_FIM% $ echo $i%OUT_INI% 5%OUT_FIM% $ echo $((i*=3))%OUT_INI% 15%OUT_FIM% $ echo $i%OUT_INI% 15%OUT_FIM% $ echo $((i%=2))%OUT_INI% 1%OUT_FIM% $ echo $i%OUT_INI% 1%OUT_FIM% %TERMINAL_FIM% Estas três operações seriam o mesmo que: <verbatim> i=$((i+3)) i=$((i*3)) i=$((i%2)) </verbatim> E isto seria válido para todos os operadores aritméticos o que em resumo produziria a tabela a seguir: <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *Expansão Aritmética* || | *Expressão* | *Resultado* | | =id++ id--= | pós-incremento e pós-decremento de variáveis | | =++id -–id= | pré-incremento e pré-decremento de variáveis | | =**= | exponenciação | | =* / %= | multiplicação, divisão, resto da divisão | | =+ -= | adição, subtração | | =<= >= < >= | comparação | | === !== | igualdade, desigualdade | | =&&= | E lógico | | =||= | OU lógico | </center> Mas o auge desta forma de construção com duplo parênteses é o seguinte: %TERMINAL_INI% $ echo $var%OUT_INI% 50%OUT_FIM% $ var=$((var>40 ? var-40 : var+40)) $ echo $var%OUT_INI% 10%OUT_FIM% $ var=$((var>40 ? var-40 : var+40)) $ echo $var%OUT_INI% 50%OUT_FIM% %TERMINAL_FIM% Este tipo de construção deve ser lido da seguinte forma: caso a variável =var= seja maior que =40= (=var>40=), então (=?=) faça =var= igual a =var= menos =40= (=var-40=), senão (=:=) faça =var= igual a =var= mais =40= (=var+40=). O que quis dizer é que os caracteres ponto-de-interrogação (=?=) e dois-pontos (=:=) fazem o papel de "então" e "senão", servindo desta forma para montar uma operação aritmética condicional. Da mesma forma que usamos a expressão =$((...))= para fazer operações aritméticas, também poderíamos usar a intrínseca (_built-in_) =let= ou construção do tipo =$[...]=. Os operadores são os mesmos para estas três formas de construção, o que varia um pouco é a operação aritmética condicional com o uso do =let=. Vejamos como seria: %TERMINAL_INI% $ echo $var%OUT_INI% 50%OUT_FIM% $ let var='var>40 ? var-40 : var+40' $ echo $var%OUT_INI% 10%OUT_FIM% $ let var='var>40 ? var-40 : var+40' $ echo $var%OUT_INI% 50%OUT_FIM% %TERMINAL_FIM% ---+++ Baseando Se você quiser trabalhar com bases diferentes da decimal, basta usar o formato: <verbatim> base#numero </verbatim> Onde =base= é um número decimal entre 2 e 64 representando o sistema de numeração, e =numero= é um número no sistema numérico definido por =base=. Se =base#= for omitida, então =10= é assumida como _default_. Os algarismos maiores que 9 são representados por letras minúsculas, maiúsculas, @ e _, nesta ordem. Se =base= for menor ou igual a 36, maiúsculas ou minúsculas podem ser usadas indiferentemente para definir algarismos maiores que 9 (não está mal escrito, os algarismos do sistema hexadecimal, por exemplo, variam entre 0 (zero) e F). Vejamos como isso funciona: %TERMINAL_INI% $ echo $[2#11]%OUT_INI% 3%OUT_FIM% $ echo $((16#a))%OUT_INI% 10%OUT_FIM% $ echo $((16#A))%OUT_INI% 10%OUT_FIM% $ echo $((2#11 + 16#a))%OUT_INI% 13%OUT_FIM% $ echo $[64#a]%OUT_INI% 10%OUT_FIM% $ echo $[64#A]%OUT_INI% 36%OUT_FIM% $ echo $((64#@))%OUT_INI% 62%OUT_FIM% $ echo $((64#_))%OUT_INI% 63%OUT_FIM% %TERMINAL_FIM% Nestes exemplos usei as notações =$((...))= e =$[...]= indistintamente, para demonstrar que ambas funcionam. Funciona também uma mudança automática para a base decimal, desde que você esteja usando a convenção numérica do C, isto é, em =0xNN=, o =NN= será tratado como um hexadecimal e em =0NN=, o =NN= será visto como um octal. Veja o exemplo: <kbd>Exemplo</kbd> %TERMINAL_INI% $ echo $((10)) # decimal%OUT_INI% 10%OUT_FIM% $ echo $((010)) # octal%OUT_INI% 8%OUT_FIM% $ echo $((0x10)) # hexadecimal%OUT_INI% 16%OUT_FIM% $ echo $((10+010+0x10)) # Decimal + octal + hexadecimal%OUT_INI% 64%OUT_FIM% %TERMINAL_FIM% Ah, já ia me esquecendo! As expressões aritméticas com os formatos =$((...)), $[...]= e com o comando =let= usam os mesmos operadores usados na instrução =expr=, além dos operadores unários (<code>++, --, +=, *=, ...</code>) e condicionais que acabamos de ver. ---++ Testes usando expressões regulares No <a href="https://twiki.softwarelivre.org/bin/view/TWikiBar/TWikiBarPapo004" target="_blank">Papo de Botequim 004</a>, nós falamos tudo sobre comandos condicionais, mas faltou um que não existia àquela época. Neste mesmo Papo de Botequim, na seção <a href="https://twiki.softwarelivre.org/bin/view/TWikiBar/TWikiBarPapo004#E_tome_de_test" target="_blank">E tome de test</a> nós chegamos a falar de uma construção do tipo: <verbatim> [[ Expressao ]] && cmd </verbatim> Onde o comando =cmd= será executado caso a expressão condicional =Expressao= seja verdadeira. Disse ainda que =Expressao= poderia ser estipulada de acordo com as regras de Geração de Nome de Arquivos (_File Name Generation_). A partir do bash versão 3, foi incorporado a esta forma de teste um operador representado por ==~=, cuja finalidade é fazer comparações com Expressões Regulares. <kbd>Exemplo:</kbd> %TERMINAL_INI% $ echo $BASH_VERSION # Conferindo se a versão do Bash é igual ou superior a 3.0.0%OUT_INI% 3.2.17(15)-release%OUT_FIM% $ Cargo=Senador $ [[ $Cargo =~ ^(Governa|Sena|Verea)dora?$ ]<nop>] && echo É político%OUT_INI% É político%OUT_FIM% $ Cargo=Senadora $ [[ $Cargo =~ ^(Governa|Sena|Verea)dora?$ ]<nop>] && echo É político%OUT_INI% É político%OUT_FIM% $ Cargo=Diretor $ [[ $Cargo =~ ^(Governa|Sena|Verea)dora?$ ]<nop>] && echo É político $ %TERMINAL_FIM% Vamos dar uma esmiuçada na Expressão Regular =^(Governa|Sena|Verea)dora?$=: ela casa com tudo que começa (=^=) por =Governa=, ou (=|=) =Sena=, ou (=|=) =Verea=, seguido de =dor= e seguido de um =a= opcional (=?=). O cifrão (=$=) serve para marcar o fim. Em outras palavras esta Expressão Regular casa com Governador, Senador, Vereador, Governadora, Senadora e Vereadora. ---++ Colorindo a tela Como você já havia visto no <a href="http://twiki.softwarelivre.org/bin/view/TWikiBar/TWikiBarPapo007#O_comando_tput" target="_blank">Papo de Botequim 007</a>, o comando =tput= serve para fazer quase tudo referente a formatação de tela, mas o que não foi dito é que com ele também pode-se usar cores de frente (dos caracteres) e de fundo. Existem também outras formas de fazer o mesmo, acho porém, esta que veremos agora, mais intuitiva (ou menos _“desintuitiva”_). A tabela a seguir mostra os comandos para especificarmos os padrões de cores de frente (_foreground_) ou de fundo (_background_): <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *Obtendo cores com o comando tput* || | *Comando* | *Efeito* | | =tput setaf n= | Especifica =n= como a cor de frente (_foreground_) | | =tput setab n= | Especifica =n= como a cor de fundo (_background_) | </center> Bem, agora você já sabe como especificar o par de cores, mas ainda não sabe as cores. A tabela a seguir mostra os valores que o =n= (da tabela anterior) deve assumir para cada cor: <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *Valores das cores com o comando tput* || | *Valor* | *Cor* | | 0 | Preto | | 1 | Vermelho | | 2 | Verde | | 3 | Marrom | | 4 | Azul | | 5 | Púrpura | | 6 | Ciano | | 7 | Cinza claro | </center> Neste ponto você já pode começar a brincar com as cores. - Mas peraí, ainda são muito poucas! - É, tem toda razão... O problema é que ainda não lhe disse que se você colocar o terminal em modo de ênfase (=tput bold=), estas cores geram outras oito. Vamos montar então a tabela definitiva de cores: <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *Valores das cores com o comando tput* ||| | *Valor* | *Cor* | *Cor após tput bold* | | 0 | Preto | Cinza escuro | | 1 | Vermelho | Vermelho claro | | 2 | Verde | Verde claro | | 3 | Marron | Amarelo | | 4 | Azul | Azul Brilhante | | 5 | Púrpura | Rosa | | 6 | Ciano | Ciano claro | | 7 | Cinza claro | Branco | </center> <kbd>Exemplo</kbd> Como exemplo, vejamos um _script_ que mudará a cor de sua tela de acordo com sua preferência. %TERMINAL_INI% $ cat mudacor.sh%OUT_INI% #!/bin/bash tput sgr0 clear # Carregando as 8 cores básicas para um vetor Cores=(Preto Vermelho Verde Marrom Azul Púrpura Ciano "Cinza claro") # Listando o menu de cores echo " Opc Cor === ===" # A linha a seguir significa: para i começando de 1; #+ enquanto i menor ou igual ao tamanho do vetor Cores; #+ incremente o valor de i de 1 em 1 for ((i=1; i<=${#Cores[@]}; i++)) { printf "%02d %s\n" $i "${Cores[i-1]}" } CL= until [[ $CL <nop>== 0[1-8] || $CL <nop>== [1-8] ]] do read -p " Escolha a cor da letra: " CL done # Para quem tem bash a partir da versao 3.2 #+ o test do until acima poderia ser feito #+ usando-se Expressoes Regulares. Veja como: #+ until [[ $CL =~ 0?[1-8] ]] #+ do #+ read -p " #+ Escolha a cor da letra: " CL #+ done CF= until [[ $CF <nop>== 0[1-8] || $CF <nop>== [1-8] ]] do read -p " Escolha a cor de fundo: " CF done let CL-- ; let CF-- # Porque as cores variam de zero a sete tput setaf $CL tput setab $CF clear%OUT_FIM% %TERMINAL_FIM% ---++ Ganhando o jogo com mais coringas Estava eu lendo meus e-mails quando recebo um do Tiago enviado para a <a href="http://br.groups.yahoo.com/group/shell-script/" target="_blank">lista de Shell Script</a> (já falei da lista e do Tiago no <a href="https://twiki.softwarelivre.org/bin/view/TWikiBar/TWikiBarBirinaite#Rotat%F3rio_Peczenyj" target="_blank">Rotatório Peczenyj</a>). A seguir o conteúdo do e-mail: Não sei se é conhecimento de todos mas o shell possui, alem do _globbing_ normal (a expansão =*=, =?= e =[a-z]= de nomes de arquivos e diretórios), um _globbing_ extendido. Acho que, em alguns casos, podera ser BEM util, eliminando um _pipe_ para um =grep= por exemplo. São eles: <verbatim> ?(padrao) </verbatim> Casa zero ou uma ocorrência de um determinado =padrao= <verbatim> *(padrao) </verbatim> Casa zero ou mais ocorrências de um determinado =padrao= <verbatim> +(padrao) </verbatim> Casa uma ou mais ocorrências de um determinado =padrao= <verbatim> @(padrao) </verbatim> Casa com exatamente uma ocorrência de um determinado =padrao= <verbatim> !(padrao) </verbatim> Casa com qualquer coisa, exceto com =padrao= Para poder utilizá-lo precisa executar o =shopt= conforme o exemplo abaixo: %TERMINAL_INI% $ shopt -s extglob $ ls%OUT_INI% file filename filenamename fileutils%OUT_FIM% $ ls file?(name)%OUT_INI% file filename%OUT_FIM% $ ls file*(name)%OUT_INI% file filename filenamename%OUT_FIM% $ ls file+(name)%OUT_INI% filename filenamename%OUT_FIM% $ ls file@(name)%OUT_INI% filename%OUT_FIM% $ ls file!(name) # divertido esse%OUT_INI% file filenamename fileutils%OUT_FIM% $ ls file+(name|utils)%OUT_INI% filename filenamename fileutils%OUT_FIM% $ ls file@(name|utils) # "lembra" um {name,utils}%OUT_INI% filename fileutils%OUT_FIM% %TERMINAL_FIM% ---++ Usando o awk para pesquisar por equivalência Aí vai mais uma que o Tiago mandou para a <a href="http://br.groups.yahoo.com/group/shell-script/" target="_blank">lista de Shell Script do Yahoo</a> (já falei da lista e do Tiago no <a href="https://twiki.softwarelivre.org/bin/view/TWikiBar/TWikiBarBirinaite#Rotat%F3rio_Peczenyj" target="_blank">Rotatório Peczenyj</a> e no <a href="https://twiki.softwarelivre.org/bin/view/TWikiBar/TWikiBarBirinaite#Ganhando_o_jogo_com_mais_coringa target="_blank">Ganhando o jogo com mais coringa</a>) Quem ja não passou por isso: Procurar uma palavra, porém uma letra acentuada, ou não, atrapalhou a busca? Não descobri como fazer o =grep= ou =sed= aceitarem algo semelhante, mas o gawk aceita classes de equivalência! Melhor explicar com um exemplo, onde vou listar o número da linha e a ocorrência casada: %TERMINAL_INI% $ cat dados%OUT_INI% éco eco èco êco ëco eço%OUT_FIM% $ awk '/^eco/{print NR,$1}' dados%OUT_INI% 2 eco%OUT_FIM% $ awk '/^e<nop>[<nop>[=c=]]o/{print NR,$1}' dados%OUT_INI% 2 eco 6 eço%OUT_FIM% $ awk '/^<nop>[<nop>[=e=]]co/{print NR,$1}' dados%OUT_INI% 1 éco 2 eco 3 èco 4 êco 5 ëco%OUT_FIM% %TERMINAL_FIM% Ou seja, usar <code>[=X=]</code> permite que a expressão encontre a letra <code>X</code> estando acentuada ou não (é sensivel à localização corrente!). A sintaxe é parecida com a das classes POSIX, trocando os dois-pontos =(:)= antes e depois da classe por sinais de igual <code>(=)</code>. Achei curioso e deve servir para algum caso semelhante ao descrito. ---++ find – Procurando arquivo por características Se você está como o Mr. Magoo, procurando em vão um arquivo, use o comando =find= que serve para procurar arquivos não só pelo nome, como por diversas outras características. Sua sintaxe é a seguinte: <verbatim> find [caminho ...] expressão [ação] </verbatim> Parâmetros: =caminho= Caminhos de diretório a partir do qual (porque ele é recursivo, sempre tentará entrar pelos subdiretórios "pendurados" neste) irá procurar pelos arquivos;<br> =expressão= Define quais critérios de pesquisa. Pode ser uma combinação entre vários tipos de procura;<br> =ação= Define que ação executar com os arquivos que atender aos critérios de pesquisa definidos por =expressão=. Os principais critérios de pesquisa definidos por expressão são: %TABLE{ sort="off" tableborder="0" cellpadding="0" cellspacing="0" headerbg="#FFFFFF" headercolor="#FFFF00" databg="#FFFFFF" headerrows="0" footerrows="0"}% | =-name= | Procura arquivos que tenham o nome especificado. Aqui podem ser usados metacaracteres ou caracteres curingas, porém estes caracteres deverão estar entre aspas, apóstrofos ou imediatamente precedidos por uma contrabarra isso porque quem tem de expandir os coringas é o =find=. Se fosse o Shell que os expandisse, isto seria feito somente com relação ao diretório corrente, o que jogaria por terra a característica recursiva do =find=; || | =-user= | Procura arquivos que tenham usuário como dono; || | =-group= | Procura arquivos que tenham grupo como grupo dono; || | =-type c= | Procura por arquivos que tenham o tipo =c=, correspondente à letra do tipo do arquivo. Os tipos aceitos estão na tabela a seguir: || || __Valores de c__ | __Tipo de arquivo procurado__ | || =b= | Arquivo especial acessado a bloco | || =c= | Arquivo especial acessado a caractere | || =d= | Diretório | || =p= | _Named pipe_ (FIFO) | || =f= | Arquivo normal | || =l= | _Link_ simbólico | || =s= | _Socket_ | | =-size ±n[unid]= | Procura por arquivos que usam mais =(+n)= de =n= unidades =unid= de espaço ou a menos =(-n)= de =n= unidades =unid= de espaço. || || __Unidades__ | __Valor__ | || =b= | Bloco de 512 _bytes_ (valor _default_) | || =c= | Caracteres | || =k= | Kilobytes (1024 _bytes_) | || =w= | Palavras (2 _bytes_) | | =-atime ±d= | Procura por arquivos que foram acessados há mais =(+d)= de =d= dias ou a menos =(-d)= de =d= dias; || | =-ctime ±d= | Procura por arquivos cujo status mudou há mais =(+d)= de =d= dias ou a menos =(-d)= de =d= dias; || | =-mtime ±d= | Procura por arquivos cujos dados foram modificados há mais =(+d)= de =d= dias ou a menos =(-d)= de =d= dias; || Para usar mais de um critério de pesquisa, faça:<br> =expressão1 expressão2= <br> ou<br> =expressão1 –a expressão2= <br> para atender aos critérios especificados por =expressão1= *e* =expressão2=; <br> =expressão1 –o expressão2= <br> para atender aos critérios especificados por =expressão1= *ou* =expressão2=. As principais ações definidas para =ação= são: %TABLE{ sort="off" tableborder="0" cellpadding="0" cellspacing="0" headerbg="#FFFFFF" headercolor="#FFFF00" databg="#FFFFFF" headerrows="0" footerrows="0"}% | =-print= | Esta opção faz com que os arquivos encontrados sejam exibidos na tela. Esta é a opção _default_ no Linux. Nos outros sabores Unix que conheço, se nenhuma ação for especificada, ocorrerá um erro; | | =-exec cmd {} \;= | Executa o comando =cmd=. O escopo de comando é considerado encerrado quando um ponto-e-vírgula =(;)= é encontrado. A cadeia ={}= é substituída pelo nome de cada arquivo que satisfaz ao critério de pesquisa e a linha assim formada é executada. Assim como foi dito para a opção =–name=, o ponto-e-vírgula =(;)= deve ser precedido por uma contrabarra =(\)=, ou deve estar entre aspas ou apóstrofos; | | =-ok cmd {} \;= | O mesmo que o anterior porém pergunta se pode executar a instrução =cmd= sobre cada arquivo que atende ao critério de pesquisa; | | =-printf formato = | Permite que se escolha os campos que serão listados e formata a saída de acordo com o especificado em =formato=. | <kbd> Exemplos: </kbd> Para listar na tela =(-print)= todos os arquivos, a partir do diretório corrente, terminados por =.sh=, faça: %TERMINAL_INI% $ find . -name \*.sh Ação não especificada –print é default%OUT_INI% ./undelete.sh ./ntod.sh%OUT_FIM% estes quatro primeiros arquivos foram%OUT_INI% ./dton.sh%OUT_FIM% encontrados no diretório corrente.%OUT_INI% ./graph.sh ./tstsh/cotafs.sh ./tstsh/data.sh%OUT_FIM% Estes quatro foram encontrados no%OUT_INI% ./tstsh/velha.sh%OUT_FIM% diretório tstsh, sob o diretório corrente%OUT_INI% ./tstsh/charascii.sh%OUT_FIM% %TERMINAL_FIM% Preciso abrir espaço em um determinado _file system_ com muita urgência, então vou remover arquivos com mais de um megabyte e cujo último acesso foi há mais de 60 dias. Para isso, vou para este _file system_ e faço: %TERMINAL_INI% $ find . –type f –size +1000000c –atime +60 –exec rm {} \; %TERMINAL_FIM% Repare que no exemplo acima usei três critérios de pesquisa, a saber: %TABLE{ sort="off" tableborder="0" cellpadding="0" cellspacing="0" headerbg="#FFFFFF" headercolor="#FFFF00" databg="#FFFFFF" headerrows="0" footerrows="0"}% | =-type f= | Todos os arquivos regulares (normais) | | =-size +1000000c= | Tamanho maior do que 1000000 de caracteres =(+1000000c)= | | =-atime +60= | Último acesso há mais de 60 =(+60)= dias. | Repare ainda que entre estes três critérios foi usado o conector __e__, isto é, arquivos regulares __e__ maiores que 1MByte __e__ sem acesso há mais de 60 dias. Para listar todos os arquivos do disco terminados por =.sh= ou =.txt=, faria: %TERMINAL_INI% $ find / -name \*.sh –o –name \*.txt –print %TERMINAL_FIM% Neste exemplo devemos salientar além das contrabarras =(\)= antes dos asteriscos =(*)=, o uso do =–o= para uma ou outra extensão e que o diretório inicial era o raiz =(/)=; assim sendo, esta pesquisa deu-se no disco inteiro (o que freqüentemente é bastante demorado). Com o =printf= é possível formatar a saída do comando =find= e especificar os dados desejados. A formatação do =printf= é muito semelhante à do mesmo comando na linguagem C e interpreta caracteres de formatação precedidos por um símbolo de percentual =(%)=. Vejamos seus efeitos sobre a formatação: <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *Caractere* | *Significado* | | =%f= | Nome do arquivo (caminho completo não aparece) | | =%F= | Indica a qual tipo de file system o arquivo pertence | | =%g= | Grupo ao qual o arquivo pertence | | =%G= | Grupo ao qual o arquivo pertence (GID- Numérico) | | =%h= | Caminho completo do arquivo (tudo menos o nome) | | =%i= | Número do inode do arquivo (em decimal) | | =%m= | Permissão do arquivo (em octal) | | =%p= | Nome do arquivo | | =%s= | Tamanho do arquivo | | =%u= | Nome de usuário (username) do dono do arquivo | | =%U= | Número do usuário (UID) do dono do arquivo | </center> Também é possível formatar datas e horas obedecendo às tabelas a seguir: <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="1" footerrows="0" }% | *Caractere* | *Significado* | | =%a= | Data do último acesso | | =%c= | Data de criação | | =%t= | Data de alteração | </center> Os três caracteres anteriores produzem uma data semelhante ao do comando =date=. Veja um exemplo: %TERMINAL_INI% $ find . -name ".b*" -printf '%t %p\n'%OUT_INI% Mon Nov 29 11:18:51 2004 ./.bash_logout Tue Nov 1 09:44:16 2005 ./.bash_profile Tue Nov 1 09:45:28 2005 ./.bashrc Fri Dec 23 20:32:31 2005 ./.bash_history%OUT_FIM% %TERMINAL_FIM% Nesse exemplo, o =%p= foi o responsável por colocar os nomes dos arquivos. Caso fosse omitido, somente as datas seriam listadas. Observe ainda que ao final foi colocado um =/n=. Sem ele não haveria salto de linha e a listagem anterior seria uma grande tripa. Essas datas também podem ser formatadas, para isso basta passar as letras da tabela anterior para maiúsculas (=%A=, =%C= e =%T=) e usar um dos formatadores das duas tabelas a seguir: <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *Tabela de formatação de tempo* || | *Caractere* | *Significado* | | =H= | Hora (00..23) | | =I= | Hora (01..12) | | =k= | Hora (0..23) | | =l= | Hora (1..12) | | =M= | Minuto (00..59) | | =p= | AM or PM | | =r= | Horário de 12 horas (hh:mm:ss) seguido de AM ou PM | | =S= | Segundos (00 ... 61) | | =T= | Horário de 24-horas (hh:mm:ss) | | =Z= | Fuso horário (na Cidade Maravilhosa BRST) | </center> <center> %TABLE{ sort="off" tableborder="0" cellpadding="4" cellspacing="1" headerbg="#0000FF" headercolor="#FFFF00" databg="#BBBBBB,#DDDDDD" headerrows="2" footerrows="1" }% | *Tabela de formatação de datas* || | *Caractere* | *Significado* | | =a= | Dia da semana abreviado (Dom...Sab) | | =A= | Dia da semana por extenso (Domingo...Sábado) | | =b= | Nome do mês abreviado (Jan...Dez) | | =B= | Dia do mês por extenso (Janeiro...Dezembro) | | =c= | Data e hora completa (Fri Dec 23 15:21:41 2005) | | =d= | Dia do mês (01...31) | | =D= | Data no formato mm/dd/aa | | =h= | Idêntico a b | | =j= | Dia seqüencial do ano (001…366) | | =m= | Mês (01...12) | | =U= | Semana seqüencial do ano. Domingo como 1º dia da semana (00...53) | | =w= | Dia seqüencial da semana (0..6) | | =W= | Semana seqüencial do ano. Segunda-feira como 1º dia da semana (00...53) | | =x= | Representação da data no formato do país (definido por $LC_ALL) | | =y= | Ano com 2 dígitos (00...99) | | =Y= | Ano com 4 dígitos | </center> Para melhorar a situação, vejamos uns exemplos; porém, vejamos primeiro quais são os arquivos do diretório corrente que começam por =.b=: %TERMINAL_INI% $ ls -la .b*%OUT_INI% -rw------- 1 d276707 ssup 21419 Dec 26 17:35 .bash_history -rw-r--r-- 1 d276707 ssup 24 Nov 29 2004 .bash_logout -rw-r--r-- 1 d276707 ssup 194 Nov 1 09:44 .bash_profile -rw-r--r-- 1 d276707 ssup 142 Nov 1 09:45 .bashrc%OUT_FIM% %TERMINAL_FIM% Para listar esses arquivos em ordem de tamanho, podemos fazer: %TERMINAL_INI% $ find . -name ".b*" -printf '%s\t%p\n' | sort -n%OUT_INI% 24 ./.bash_logout 142 ./.bashrc 194 ./.bash_profile 21419 ./.bash_history%OUT_FIM% %TERMINAL_FIM% No exemplo que acabamos de ver, o =\t= foi substituído por um =<TAB>= na saída de forma a tornar a listagem mais legível. Para listar os mesmos arquivos classificados por data e hora da última alteração: %TERMINAL_INI% $ find . -name ".b*" -printf '%TY-%Tm-%Td %TH:%TM:%TS %p\n' | sort%OUT_INI% 2004-11-29 11:18:51 ./.bash_logout 2005-11-01 09:44:16 ./.bash_profile 2005-11-01 09:45:28 ./.bashrc 2005-12-26 17:35:13 ./.bash_history%OUT_FIM% %TERMINAL_FIM% Vou aproveitar também para mandar o meu jabá: diga para os amigos que quem estiver afim de fazer um curso porreta de programação em _Shell_ que mande um e-mail para a nossa <a href="mailto:contato@clavis.com.br?Subject=Curso de Shell com Julio Neves">gerencia de treinamento</a> para informar-se. Qualquer dúvida ou falta de companhia para um chope ou até para falar mal dos políticos é só mandar um e-mail para <a href="mailto:julioneves@openoffice.org?Subject=Dúvidas Papo de Botequim">mim</a>. Valeu!
E
dit
|
A
ttach
|
P
rint version
|
H
istory
: r26
<
r25
<
r24
<
r23
<
r22
|
B
acklinks
|
V
iew topic
|
M
ore topic actions
Topic revision: r26 - 01 Aug 2009 - 22:35:11 -
BrSalgado
TWikiBar
Página Inicial
Últimas alterações
Índice
Procurar
Estatísticas de Uso
Aviso de Atualização
Configurações Gerais
Projeto Gráfico
Mapa do Site
Quem Somos
Registre-se
?
Regras de Formatação
Biblioteca Gráfica
?
Carinhas Gráficas
Webs Wiki-SL
Amadeu
Anapolivre
ArquivoLivre
Arte
BahiaSocial
BeaBa
BibliotecaLivre
Blogs
BrasilDigital
BrasilELivre
BSM
Ccsa
CESL
CoberturaWiki
Cooperativas
Curriculo
DarvinMarosin
DiaD
Dinamicoop
Economia
EconomiaSolidaria
EducacaoLivre
Ekaaty
Emacsbr
ENSL
Fatos
Festival3
Festival4
Flisol
Fmpb
Formatos
Foswikibr
FSM2005
GNOMEBR
GTTemario2004
GTWeb
Guialivre
HDC
Incubus
InkscapeBrasil
Jogos
KdeBR
KSP
LGM
LinuxStokDoc
Livros
Main
Mentores
MHHOB
MinuanoDigital
MoradiaECidadania
OlhosDagua
Olimpo
OLPC
OOPTQ
Papers
PCLivre
PentahoBrasil
Pessoas
Portal
Prefeituras
PSLAL
PSLBA
PSLBancarios
PSLBrasil
PSLGO
PSLMA
PSLMG
PSLMIP
PSLMT
PSLMulheres
PSLPI
PubFisl10
PubFisl7
PubFisl8
PubFisl9
QuilomboDoSopapo
RadioSL
RedeMesh
RedePopular
RobotWars
Sandbox
Saudelivre
Scribus
Sementes
Shakya
SLRJ
SoftwareLivreIrece
SoftwareLivreVS
SoLiSC
SuporteLivre
System
Telecentros
TeseSA
TextoLivre
TV
TWikiBar
TWikiPtbr
UNELivre
UNIMIX
VilaTorres
WebNordeste
WTRD2004
Este Menu
?skin=free
English
Español
Português brasileiro
Copyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki-SL?
Send feedback