データファイルの送信を自動化するためにbashスクリプトを設定しようとしています。望ましい最終結果は、指定されたディレクトリを週に1回チェックし、過去2日以内に変更されたすべてのExcelファイルのリストを作成し、指定された受信者リストに電子メールで送信することです。
Sendmailを構成して機能させています(Gmailリレーを使用)。私はmuttを設定して動作しています。一般的に、送信しようとしているコマンドは、CLIから直接送信すると機能します(添付ファイル付きでメールが送受信されます)が、スクリプトからそれらを呼び出そうとすると、繰り返し失敗します。ディレクトリとファイル名にスペースが含まれているという事実からすべてが生じているようです。これを変更することはできません-ファイルの名前を制御することはできません-しかし、これが私のスクリプトのこだわりのようです。
2つの主な問題:
Can't stat "/path\ to/file1.xls
/path\ to/file2.xls": No such file or directory
改行文字は$ FILES変数の一部として渡されています。
/path/to/the\ files/file\ 1.xls
は3つの値( /path/to/the\
、files/file\
、1.xls
)。スクリプトの前半は機能していましたが(すべてのファイルを一度にメールで送信)、ループを追加しようとしてスクリプトを中断することができました。もちろん、以前の作業バージョンは保存しませんでした。区切り文字を正しく取得するためにループにset ifs=$'\n'
を使用してみましたが、適切な位置にある場合、muttはファイルへのフルパスがファイルではないか、指定された受信者がいないことを示しています。それは少し腹立たしいです。
脚本:
#!/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
ファイルを個別に送信するために、上記の最後の2行の代わりに次のことを試しました。
# 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
何か助け?私はしばらくこれにこだわっています。別の言語でこれを行う方が簡単な場合、私はmuttを使用することと結婚していません。
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
それらを一度に1つずつメールで送信するには、echo
行を次のように変更します。
for mailf do echo "$MBODY" |
mutt -s "Data files for $TODAY" -a "$mailf" -- $EMAILS
done
おそらくうまくいくでしょうが、あなたの本当の問題はここにあります:
...
set IFS=...
...
これは、内部フィールドセパレータの値にはまったく影響しませんが、値IFS=...
を最初の位置パラメータまたは$1
に割り当てます。 $IFS
は、以前の値のままですset
$1
。あなたはただする必要があります:
IFS='
'
または...
IFS=${IFS# }
..._$IFS
がデフォルト値に設定されている場合、これが実行可能なスクリプトであり、スクリプトの他の場所で$IFS
を変更していない場合は、デフォルト値でなければなりません。
問題は、$FILE
が配列ではないが、そのままアクセスしていることです。
for datafile in ${FILES[*]}
それは巨大な文字列を返すだけなので、すべてのファイルを一度に返します。
これを回避するには、$FILE
の各ファイルに改行を追加し、ループ内でecho -e
を使用して各行を個別に返します。
# Get the list of files to send
FILES=$(find "$DIR" -type f -mtime -2 | sed 's/ /\\ /g' | grep ".xls" | sed '/.xls$/ a\\\n')
その後
for datafile in $(echo -e $FILES)
do
echo $MBODY | mutt -s \"Data files for $TODAY\" -a "$datafile" -- $EMAILS
done
さらに、$datafile
に二重引用符を追加すると、ファイル名の空白に関する問題が修正されます
作業方法を考え出したようです。 $IFS
を回避し、ディレクトリリスト全体を文字列または配列として処理しようとしないでください。見つかった各エントリを処理します。最後のループを次のものに置き換えました:
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 {} +
ここで、$NOBODY
は、一致するファイルが見つからなかったことを示す別のメッセージ本文です。 "*.xlsx*"
を使用して、.xlsファイルと.xlsxファイルの両方が確実に一致するようにします。これは機能しましたが、上記で宣言された変数はループに渡されませんでした。それぞれの前にexport
コマンドを付けました。その後、上記でうまくいきます。 (変数をループに入れるより洗練された方法がある場合、私はそれを見つけられませんでした。ループと変数に関する議論の99%は、それらをループに戻すのではなく、ループから戻す方法に関するものです。)