paulo1205
(usa Ubuntu)
Enviado em 23/04/2018 - 22:42h
mazinatti escreveu:
Boa Noite Senhores..
Tente cada um dos métodos colocado aqui.. o mais rápido foi o perl, entretanto eu nao sei "ainda" trabalhar com o perl.. então resolvi o problema com o cat.. leio agora o arquivo inteiro e vou salvando em um arquivo novo jah formatado.
Bem, eu acho Perl tranquilo porque em muito ele lembra outra ferramentas comuns, como se fosse uma mistura de shell, awk, grep e sed. De todo modo, a escolha cabe a você, obviamente.
Permita-me fazer alguns comentários no seu código.
#!/bin/bash
criaCam1="1MAIUSC"
criaCam2="1minusc"
criaCam3="1Pmaiusc"
criaCam4="1VtUdOm"
criaCam5="1VTuMiN"
i=100
#Sempre que rodo limpa os arquivos antigos
echo > $criaCam1
echo > $criaCam2
echo > $criaCam3
echo > $criaCam4
echo > $criaCam5
Na verdade, o que você faz nas linhas cima é colocar uma linha em branco e cada um desses arquivos. Se seu objetivo é remover conteúdo anterior, troque o comando
echo pelo comando
: (apenas o sinal de “:”, que é um comando que não faz nada), ou não coloque comando nenhum, apenas o redirecionamento.
Isso pode ser problemático se:
• houver arquivos cujos nomes contenham espaços, tabulações ou quebras de linha;
• houver muitos arquivos, pois vai consumir muita memória do processo que está rodando o script;
• não houver nenhum arquivo na forma
*.txt (com as configurações mais comuns, isso ocasionaria erro no shell ou por parte de
ls[ );
• a lista for vazia (dependeria de preparar o shell para não cair no caso anterior, mas tem seus próprios impactos, especialmente neste seu
script , conforme discutido mais abaixo).
Não bastassem esses problemas condicionais, você chama
ls inutilmente, uma vez que é o próprio shell que expande o padrão “*.txt”.
Seria mais simples e eficiente fazer simplesmente “
lista=( *.txt ) ”, e isso ainda teria o benefício adicional de evitar o problema de arquivos com nomes contendo espaços. Infelizmente, isso não resolve o gasto de memória provocado por uma eventual lista gigantesca de arquivos (mesmo em princípio preferindo evitar comandos externos, quando eu não necessariamente tenho controle sobre a quantidade de arquivos a ser processada, eu prefiro não trazer toda a lista para a memória de uma só vez, e mais abaixo eu mostrou um jeito de assim fazer).
Para preparar o shell para a possibilidade de não haver nenhum arquivo com sufixo “.txt”, o certo seria executar anteriormente os seguintes comandos (que se aplicam tanto ao uso que você fez de
ls quanto à forma alternativa que lhe sugiro).
set +o noglob
shopt -u failglob
shopt -s nullglob
Qual a utilidade desse comando? Porque o que ele faz é mostrar na saída padrão o conteúdo do primeiro elemento da lista, só que de forma ordenada. Se você rodar o script no terminal, a saída será na tela. Tem certeza de que é isso que você quer?
Mas mais problemático ainda é quando o conteúdo de
lista é vazio (o terceiro caso da observação anterior). Nessa situação, o comando
sort vai esperar que os dados a serem ordenados venham da entrada padrão. Se for executado no terminal, o
script vai esperar que você digite tal conteúdo. Tenho quase certeza de que você não quer que isso aconteça.
echo "existem ${#lista} Arquivos em formato *txt na pasta" `ls *.txt`
Se você já tem a lista, para que rodar novamente o comando
ls ? Você poderia conseguir o mesmo efeito com algo como “
echo "..." *.txt ” ou “
echo "... $lista[*]" ”.
Contudo, fica a pergunta: você precisa realmente exibir a lista?
for ((N=0;N<${#lista[*]};N++));do
Impressão da mensagem à parte, e ignorando a ordenação do primeiro arquivo, que me pareceu um acidente de percurso, uma forma mais segura iterar sobre os elementos da lista de arquivos seria a seguinte.
# Roda o comando find no diretório corrente (.), utilizando um byte nulo como separador
# de nomes (pois é possível teoricamente ter um arquivo com quebra de linha no meio do nome)
# e joga a saída no file descriptor de nº 3, redirecionado-o como fd de entrada de dados.
exec 3< <(find . -maxdepth1 -iname "*.txt" -print0)
# N deixa de ser uma variável de controle da iteração, e passa a ser incrementada manualmente.
N=0
# Itera pelos dados recebidos do find, mandando ler do descritor 3 (-u 3) e usar
# o byte nulo como separador dos registros (-d $'\0').
while read -u 3 -d $'\0' arquivo; do
Dentro do bloco a ser repetido, em lugar de “
${lista[N]} ” você usaria “
"$arquivo" ”.
#faço uma cópia do arquivo original
cp ${lista[N]} $N
Para que essa cópia? Depois você vai apagá-la, e você não mexe nos arquivos originais. Assim sendo, você poderia usar os arquivos originais como base para as transformações que quisesse fazer.
#Verifico se o arquivo possui algum conteudo
if [ -s "${lista[N]}" ]
then
Você não deveria ter feito essa verificação antes de copiar o arquivo (supondo que você tenha mesmo de copiá-lo, que não parece ser o caso)?
#Aviso com qual arquivo vou trabalhar
echo 'Trabalho o arquivo '${lista[N]}' agora com '`wc -l ${lista[N]}` 'linhas'
Maiuscula=$(cat ${N} | awk '{ print toupper($0); }') #todas as letras carregadas maiusculas
Minuscula=$(cat ${N} | awk '{ print tolower($0); }') #Todas as letras minusculas
PMaiuscula=$(echo $Minuscula | sed 's/(\b.)/\U&/g') #A primeira letra Sempre Maiuscula
Para os dois primeiros, se o objetivo é somente converte de maiúsculas para minúsculas, em vez de
awk você poderia usar
tr , que é potencialmente mais simples e mais leve. E não precisaria de dois programas externos (cat ... | awk ...), mas apenas de um deles, como no exemplo abaixo.
Maiuscula=$(tr '[:lower:]' '[:upper:]' < "$arquivo")
Minuscula=$(tr '[:upper:]' '[:lower:]' < "$arquivo")
Note que eu coloco “
"$arquivo" ”, com a expansão da variável entre aspas, porque o nome do arquivo pode conter espaços asteriscos e outras coisas perigosas.
Contudo, eu, novamente, desaprovo essa ideia de ler todo o conteúdo para a memória de uma vez só (e triplicado, ainda por cima), porque você pode topar com um arquivo gigantesco, que pode comprometer a execução do seu programa (ou até mesmo do sistema!) ao consumir toda a memória que ele tem disponível.
echo $Maiuscula >> $criaCam1
echo $Minuscula >> $criaCam2
echo $PMaiuscula >> $criaCam3
Seu código de agora é bem diferente do que você mostrou originalmente. Antes, cada arquivo da lista de arquivos de entrada gerava um arquivo de saída, e cada palavra transformada ocupava um linha distinta nesse arquivo de saída. Neste de agora, você gera apenas três arquivos, cada um deles contendo uma linha para cada arquivo de entrada, com todas as palavras transformads de cada arquivo numa só linha.
Os dois programas só guardam relação pelas transformações feitas sobre cada palavra, mas a disposição de saída de agora não tem nada a ver com a anterior. Com essa mudança, qualquer comparação de desempenho em relação ao que se tinha antes fica prejudicada.
Fora isso, como eu sou implicante com a execução de comandos externos desnecessários e com a leitura de todos os dados para a memória de uma vez só (a não ser que haja garantias de que não se vai provocar estouro da memória), eis uma forma de fazer as transformações usando apenas recursos internos do Bash (produzindo saída como a do programa atual, não como o anterior).
# Os três redirecionamentos abaixo podem ficar fora do loop mais externo
# (o que percorre a lista de arquivos). Se assim se fizer (e assim se deveria fazer),
# os operadores de redirecionamento podem ser trocados de “>>” para “>”, e isso
# bastará para zerar seu conteúdo
exec 5>> "$criaCam1"
exec 6>> "$criaCam2"
exec 7>> "$criaCam3"
# Redireciona entrada a partir do arquivo recebido da lista de arquivos.
exec 4< "$arquivo"
first_line=0
if read -u 4 linha; then
echo -n ${linha^^} >&5 # Transforma para maiúsculas.
minusc=${linha,,} # Transforma para minúsculas.
echo -n $minusc >&6
# O bloco abaixo separa as palavras individuais e converte apenas as primeiras letras.
pmaiusc=""
while [[ "$minusc" =~ ([[:alnum:]]+[^[:alnum:]]*)(.*) ]]; do
pmaiusc+=${BASH_REMATCH[1]^}
minusc="${BASH_REMATCH[2]}"
done
echo -n $pmaiusc >&7
while read -u 4 linha; do
echo -n "" ${linha^^} >&5 # Transforma para maiúsculas.
minusc=${linha,,} # Transforma para minúsculas.
echo -n "" $minusc >&6
# O bloco abaixo separa as palavras individuais e converte apenas as primeiras letras.
pmaiusc=""
while [[ "$minusc" =~ ([[:alnum:]]+[^[:alnum:]]*)(.*) ]]; do
pmaiusc+=${BASH_REMATCH[1]^}
minusc="${BASH_REMATCH[2]}"
done
echo -n "" $pmaiusc >&7
done
fi
exec 4<&- # Fecha descritor nº 4
# Os três fechamentos de descritores abaixo podem (e deveriam) ser movidos
# para após o encerramento do loop que percorrer a lista de arquivos.
exec 7<&-
exec 6<&-
exec 5<&-
fi
rm $N
let i=i+1
done
Não entendi essa variável i . Você não a usa com propósito nenhum no seu programa.
De todo modo, se você seguiu minha sugestão de obtenção da lista de arquivos, agora tem de fechar o descritor referente ao comando externo find , usado acima.
exec 3<&-