![シェルスクリプト bash: 月に基づいてファイルを反復移動する](https://rvso.com/image/1395273/%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%20bash%3A%20%E6%9C%88%E3%81%AB%E5%9F%BA%E3%81%A5%E3%81%84%E3%81%A6%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%8F%8D%E5%BE%A9%E7%A7%BB%E5%8B%95%E3%81%99%E3%82%8B.png)
シェル スクリプトについてはほとんど知識がありませんが、残念ながら 1 つ書かなければなりません。ファイルを移動する bash スクリプトの反復について質問したいのですが、cronjob によって実行される月別にソートされたログ ファイルを移動する必要があります。計画では、mtime +30 (1 か月前) のファイルを複数のフォルダーに移動し、cronjob を毎日実行します。例:
前に
/home/Work/LogFiles/20131200012.log
/home/Work/LogFiles/thisLogIsDifferent.log
/home/Work/LogFiles/20120322222.log
/home/Work/LogFiles/20140100011.log
/home/Work/LogFiles/thisLogIsDifferent2.log
後
/home/Work/LogFiles/thisLogIsDifferent.log
/home/Work/LogFiles/thisLogIsDifferent2.log
/home/Work/LogFiles/2013/DEC/20131200012.log
/home/Work/LogFiles/2012/MAR/20120322222.log
/home/Work/LogFiles/2014/JAN/20140100011.log
どのような方法を使用すればよいのか全くわかりません。そこで、私のひどいシェル スクリプトを以下に示します。
BASE_DIR=/home/Work/LogFiles
REPORT_DIR_YEAR=$BASE_DIR/`date +%Y`
REPORT_DIR=$REPORT_DIR_YEAR/`date +%b`
NOW=$(date +"%Y%m")
if ! [ -d $REPORT_DIR_YEAR ]; then
mkdir $REPORT_DIR_YEAR
if ! [ -d $REPORT_DIR ]; then
mkdir $REPORT_DIR
fi
fi
#THIS PART NEED TO BE RE-ARRANGED
#What I expect is not date=NOW; BUT SOME KIND LIKE date %m-1? but I still don't have any ideas about modify date function.
for file in find $BASE_DIR -maxdepth 1 -type f -mtime +30 -name '*$NOW*'
do
month=$(ls -l $file | awk '{ print $6 }')
case "$month" in
"Jan") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Feb") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Mar") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Apr") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"May") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Jun") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Jul") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Aug") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Sep") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Oct") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Nov") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
"Dec") mv $file $REPORT_DIR_YEAR/$month/$file echo "$file moved to $REPORT_DIR/$file";;
*) echo " Do nothing " ;;
esac
done
はい、このケースは$month
前の for ループでは機能しません$file
。なぜでしょうか? わかりません。さまざまなソース、フォーラム、 for ループの例からコピーしただけですが、それでも機能しません。
答え1
まず第一に、それは出力を解析するのは決して良い考えではないls
さまざまな問題を引き起こす可能性があるためです。ファイルの年齢を取得するより良い方法は、 ですstat
。例:
$ ls -l 20120322222.log
-rw-r--r-- 1 terdon terdon 0 Jan 1 2012 20120322222.log
$ stat -c %y 20120322222.log
2012-01-01 00:00:00.000000000 +0100
さて、ファイルの年齢を取得する方法はわかりました。問題は、それを 3 文字の月名に変換する方法です。最も簡単な方法は、次のコードを使用することですdate
。
$ date -d "2012-01-01" +"%b"
Jan
2 つのコマンドを組み合わせると次のようになります。
$ date -d "$(stat -c %y 20120322222.log)" +"%b"
Jan
したがって、これを念頭に置いて、スクリプトを次のように記述できます。
#!/usr/bin/env bash
BASE_DIR=/home/Work/LogFiles
## Find those files that are older than a month
find "$BASE_DIR" -maxdepth 1 -mtime +30 -type f -name "20*" |
while IFS= read -r file; do
## Get the file's modification year
year="$(date -d "$(stat -c %y "$file")" +%Y)"
## Get the file's modification month
month="$(date -d "$(stat -c %y "$file")" +%b)"
## Create the directories if they don't exist. The -p flag
## makes 'mkdir' create the parent directories as needed so
## you don't need to create $year explicitly.
[[ ! -d "$BASE_DIR/$year/$month" ]] && mkdir -p "$BASE_DIR/$year/$month";
## Move the file
mv "$file" "$BASE_DIR/$year/$month"
done
上記のスクリプトは、ファイル名を解析するのではなく、ファイルの実際の変更日を取得することを前提としています。ファイル名を解析したい場合は、お知らせください。それに応じて修正します。
答え2
terdon に感謝します。彼/彼女のスクリプトを入手し、OS X で動作するように修正することができました。また、フォルダー構造を月、日、時間に基づいて変更しました。
#!/usr/bin/env bash
BASE_DIR=/Users/user/
## Find those files that are older than a month
find "$BASE_DIR" -maxdepth 1 -type f |
while IFS= read -r file; do
## Get the file's modification month
month="$(stat -f '%Sm' -t '%m' "$file")"
## Get the file's modification day
day="$(stat -f '%Sm' -t '%d' "$file")"
## Get the file's modification hour
hour="$(stat -f '%Sm' -t '%H' "$file")"
## Create the directories if they don't exist. The -p flag
## makes 'mkdir' create the parent directories as needed so
## you don't need to create $year explicitly.
[[ ! -d "$BASE_DIR/$month/$day/$hour" ]] && mkdir -p "$BASE_DIR/$month/$day/$hour";
## Move the file
mv "$file" "$BASE_DIR/$month/$day/$hour"
done
答え3
これが私の解決策です:
#!/bin/bash
BASE_DIR="${1}"
if [ -z "${BASE_DIR}" ]; then
BASE_DIR="/home/Work/LogFiles"
fi
if [ ! -d "${BASE_DIR}" ]; then
echo "Error: '${BASE_DIR}' does not exists." >2
exit 1
fi
declare -a MONTH_NAMES
MONTH_NAMES[1]='JAN'
MONTH_NAMES[2]='FEB'
MONTH_NAMES[3]='MAR'
MONTH_NAMES[4]='APR'
MONTH_NAMES[5]='MAY'
MONTH_NAMES[6]='JUN'
MONTH_NAMES[7]='JUL'
MONTH_NAMES[8]='AUG'
MONTH_NAMES[9]='SEP'
MONTH_NAMES[10]='OCT'
MONTH_NAMES[11]='NOV'
MONTH_NAMES[12]='DEC'
find "${BASE_DIR}" -maxdepth 1 -mtime +30 -type f -name '*.log' \
| grep -e '/[0-9]*.log$' \
| while read FILE; do
FILENAME="$(basename "${FILE}")"
FILE_YEAR="$(echo "${FILENAME}" | cut --bytes=1-4)"
FILE_MONTH="$(echo "${FILENAME}" | cut --bytes=5-6)"
FILE_MONTH_NAME="${MONTH_NAMES[${FILE_MONTH}]}"
REPORT_DIR="${BASE_DIR}/${FILE_YEAR}/${FILE_MONTH_NAME}"
test -d "${REPORT_DIR}" || mkdir -p "${REPORT_DIR}"
mv "${FILE}" "${REPORT_DIR}"
echo "'${FILENAME}' moved to '${REPORT_DIR}'"
done