Script para enviar por e-mail todos os arquivos correspondentes em um diretório

3

Tentando configurar um script bash para automatizar o envio de arquivos de dados. O resultado final desejado é verificar um diretório especificado uma vez por semana, fazer uma lista de todos os arquivos do Excel modificados nos últimos dois dias e enviá-los por e-mail para uma lista de destinatários designada.

Eu tenho o sendmail configurado e funcionando (usando uma retransmissão do Gmail). Eu tenho mutt configurado e funcionando. Em geral, os comandos que estou tentando enviar funcionam se enviados diretamente da CLI (o email é enviado e recebido, com anexos), mas recebo falhas repetidas ao tentar chamá-los do script. Tudo parece resultar do fato de que os nomes de diretórios e arquivos contêm espaços. Eu não posso mudar isso - eu não tenho controle sobre a nomeação dos arquivos - mas parece que este é o ponto de atrito no meu script.

Dois problemas principais:

  1. Se eu tentar enviar todos os arquivos de uma só vez, o mutt informa que o (s) anexo (s) não pode ser anexado:
Can't stat "/path\ to/file1.xls
/path\ to/file2.xls": No such file or directory

O caractere de nova linha está sendo passado como parte da variável $ FILES.

  1. Se eu tentar percorrer o diretório para enviar arquivos um de cada vez (meu resultado desejado, já que alguns arquivos são bem grandes), o script interpreta espaços como delimitadores, com ou sem escape - então /path/to/the\ files/file\ 1.xls é visto como 3 valores ( /path/to/the\ , files/file\ , 1.xls ).

Eu fiz a primeira metade do script funcionando (enviando todos os arquivos ao mesmo tempo) mas consegui quebrá-lo, tentando adicionar o loop. Claro que não salvei a versão anterior. Tentei usar set ifs=$'\n' para o loop para obter os delimitadores corretos, mas quando eu tenho que no lugar mutt diz-me o caminho completo para o arquivo não é um arquivo, ou que não havia nenhum destinatário designado. É um pouco enlouquecedor.

Script:

#!/bin/bash
# This file should send an email to specified recipients with data files for the week attached.

# Set reply-to address for Mutt
export REPLYTO="[email protected]"

# Replace with space separated email address of all recipients
EMAILS="[email protected] [email protected]"

# Get today's date for subject
# Date is in YYYY-MM-DD format
TODAY=$(date +%F)

# Set the message body
MBODY="Sending this week's data files.\n"

# Set the starting directory
# Don't bother escaping it, this is fixed in FILES variable below
DIR="/home/user/path to files"

# Get the list of files to send
FILES=$(find "$DIR" -type f -mtime -2 | sed 's/ /\ /g' | grep ".xls")

# Check to see if we found any files or not
if [ -z "$FILES" ]; then
    MBODY="No matching files added or modified within last 2 days. No files sent.\n"
    echo "$MBODY" | mutt -s "Data files for $TODAY" $EMAILS
fi

# Send all files in a single email
echo $MBODY | mutt -s "Data files for $TODAY" -a "$FILES" -- $EMAILS

Para enviar arquivos separadamente, tentei o seguinte em vez das duas últimas linhas acima:

# Cycle through FILES array and send each file individually
for datafile in ${FILES[*]}
do
   set IFS=$'\n\t'
   echo $MBODY | mutt -s \"Data files for $TODAY\" -a $datafile -- $EMAILS 
   unset $IFS
done

Qualquer ajuda? Eu estive preso nisso por um tempo agora. Eu não sou casada com o uso de vira-lata, ou até mesmo bash para esse assunto, se isso é mais fácil feito em outro idioma.

    
por dr.nixon 14.08.2014 / 02:52

3 respostas

2

set "/home/user/path to files/"*.xls
for f do [ "$f" -nt "$two_day_old_file" ] && set "$@" "$f" ; shift ; done
touch "$two_day_old_file"
echo $MBODY | mutt -s "Data files for $TODAY" -a "$@" -- $EMAILS    

Para enviá-los um de cada vez, altere a linha echo para:

for mailf do echo "$MBODY" | 
    mutt -s "Data files for $TODAY" -a "$mailf" -- $EMAILS
done

Provavelmente funcionaria, mas o seu verdadeiro problema está aqui:

...
set IFS=...
...

Isso não afeta o valor do separador de campo interno, mas atribui o valor IFS=... ao primeiro parâmetro posicional ou $1 . $IFS permanece valioso, seja lá o que fosse antes de você set $1 . Você só precisa fazer:

IFS='
   ' 

Ou ...

IFS=${IFS# } 

... se $IFS estiver definido com o valor padrão, que deve ser se este for um script executável e você não tiver alterado $IFS em qualquer outro lugar no script.

    
por 14.08.2014 / 03:24
1

O problema é $FILE não é uma matriz, mas você está acessando como é

for datafile in ${FILES[*]}

Está apenas retornando uma string gigante, daí todos os arquivos de uma só vez.

Para resolver isso, acrescente uma nova linha a cada arquivo em $FILE e, em seguida, use o loop echo -e para retornar cada linha individualmente

# Get the list of files to send
FILES=$(find "$DIR" -type f -mtime -2 | sed 's/ /\ /g' | grep ".xls" | sed '/.xls$/ a\\n')

então

for datafile in $(echo -e $FILES)
do
   echo $MBODY | mutt -s \"Data files for $TODAY\" -a "$datafile" -- $EMAILS 
done

Além disso, adicionar aspas duplas a $datafile deve corrigir o problema com espaços em branco nos nomes de arquivos

    
por 14.08.2014 / 18:45
1

Parece ter descoberto um método de trabalho. Evitando $IFS e não tentando manipular toda a lista de diretórios como uma string ou matriz - trabalhando em cada entrada conforme ela é encontrada. Substituiu o último loop pelo seguinte:

find "$DIR" -type f -mtime -2 -name '*.xls*' -exec sh -c '
  for file do
    # Check to see if we found any files or not
    if [ -z "$file" ]; then
        echo "$NOBODY" | mutt -s "Data files for $TODAY" $EMAILS
    # If we found files, then email them
    else
        echo "$MBODY" |  mutt -s "Data files for $TODAY" -a "$file" -- $EMAILS
    fi
  done
' sh {} +

em que $NOBODY é um corpo de mensagem diferente indicando que nenhum arquivo correspondente foi encontrado. Usando "*.xlsx*" para garantir que os arquivos .xls e .xlsx sejam correspondidos. Isso funcionou, mas as variáveis declaradas acima não estavam sendo passadas para o loop. Eu prefacotei cada um com um comando export , após o qual o acima faz o truque. (Se há uma maneira mais elegante de colocar as variáveis no loop, eu não as encontrei. 99% das discussões sobre loops e variáveis são sobre como recuperá-las do loop, e não para dentro dele.)

    
por 15.08.2014 / 05:41