小さなスクリプトがあり、それは単にDownloadsフォルダーを通り、拡張子に従ってファイルを並べ替えます。
どうすればこれを作成できますcleaner/better?拡張機能と対応するディレクトリのリストを維持し、コマンドを実行するだけです。 forループなので、拡張子を追加するたびに新しい行を追加する必要はありません。
現在のスクリプト:
#!/bin/sh
LOCKFILE=/tmp/.hiddensync.lock
if [ -e $LOCKFILE ]
then
echo "Lockfile exists, process currently running."
echo "If no processes exist, remove $LOCKFILE to clear."
echo "Exiting..."
exit
fi
touch $LOCKFILE
timestamp=`date +%Y-%m-%d::%H:%M:%s`
echo "Process started at: $timestamp" >> $LOCKFILE
## Move files to various subfolders based on extensions
find ~/Downloads -maxdepth 1 -name "*.pdf" -print0 | xargs -0 -I % mv % ~/Downloads/PDF/
find ~/Downloads -maxdepth 1 -name "*.opm" -print0 | xargs -0 -I % mv % ~/Downloads/OPM/
find ~/Downloads -maxdepth 1 -name "*.yml" -print0 | xargs -0 -I % mv % ~/Downloads/YML/
find ~/Downloads -maxdepth 1 -name "*.css" -print0 | xargs -0 -I % mv % ~/Downloads/CSS/
find ~/Downloads -maxdepth 1 -name "*.tar.gz" -print0 | xargs -0 -I % mv % ~/Downloads/archives/
find ~/Downloads -maxdepth 1 -name "*.Zip" -print0 | xargs -0 -I % mv % ~/Downloads/archives/
find ~/Downloads -maxdepth 1 -name "*.jpg" -print0 | xargs -0 -I % mv % ~/Downloads/Pictures/
find ~/Downloads -maxdepth 1 -name "*.png" -print0 | xargs -0 -I % mv % ~/Downloads/Pictures/
find ~/Downloads -maxdepth 1 -name "*.tiff" -print0 | xargs -0 -I % mv % ~/Downloads/Pictures/
find ~/Downloads -maxdepth 1 -name "*.pm" -print0 | xargs -0 -I % mv % ~/Downloads/Perl/
find ~/Downloads -maxdepth 1 -name "*.xls*" -print0 | xargs -0 -I % mv % ~/Downloads/Excel/
find ~/Downloads -maxdepth 1 -name "*.doc*" -print0 | xargs -0 -I % mv % ~/Downloads/Word/
echo "Task Finished, removing lock file now at `date +%Y-%m-%d::%H:%M:%s`"
rm $LOCKFILE
宛先に複数の拡張子がある場合、find
ディレクティブにロジックを追加できます。
find ~/Downloads -maxdepth 1 \( -name "*.tar.gz" -o -name "*.Zip" \) -print0 | xargs -0 -I % mv % ~/Downloads/archives/
そして、あなたはxargsにパイプする必要はありません:
find ~/Downloads -maxdepth 1 \( -name "*.tar.gz" -o -name "*.Zip" \) -exec mv -t ~/Downloads/archives/ {} +
-maxdepth 1
があるので、本当にfind
が必要ですか?
shopt -s nullglob
cd ~/Downloads
mv -t archives/ *.tar.gz *.Zip
mv -t Pictures/ *.jpg *.png *.tiff
# etc
移動するファイルがない場合、このアプローチではいくつかのエラーが発生します。あなたはそのようなものでそれを回避することができます:
shopt -s nullglob
movefiles() {
local dest=$1
shift
if (( $# > 0 )); then
mkdir -p "$dest"
mv -t "$dest" "$@"
fi
}
cd ~/Downloads
movefiles PDF/ *.pdf
movefiles OPM/ *.opm
movefiles YML/ *.yml
movefiles CSS/ *.css
movefiles archives/ *.Zip *.tar.gz
movefiles Pictures/ *.jpg *.png *.tiff
movefiles Perl/ *.pm
movefiles Excel/ *.xls*
movefiles Word/ *.doc*
ノート:
movefiles PDF/ "*.pdf"
を実行しますmovefiles PDF/
find
はmvコマンドを単独で実行できます。使用する構文は次のとおりです。
find <filter> -exec mv {} <destination>
ご了承ください: {}
はファイル名に置き換えられ、そのようにエスケープする必要がある場合があります\{\}
Bash'ismを許容する場合は、bash配列を利用してスクリプトを次のように書き直すことができます。
BASE_PATH="~/Downloads/"
# 1st element: find filter, 2d element: destination
EXT_TO_PATH=(
'a=( "-iname \"*.pdf\" " "PDF" )'
'a=( "-iname \"*.jpg\" -o -iname \"*.png\" " "Pictures" )'
)
for elt in "${EXT_TO_PATH[@]}" ; do
eval $elt
echo "Extensions: ${a[0]}"
echo "Destination: ${a[1]}"
find $BASE_PATH -maxdepth=1 ${a[0]} -exec mv {} ${BASE_PATH}/${a[1]}
done
タスクはべき等なので、この場合はそれほど問題になりませんが、ロックが正しくありませんであり、競合状態の影響を受けます。
ロックファイルの存在を確認し、2つの異なる(非アトミック)操作を使用して作成します。このため、スクリプトの2つのインスタンスが同時に起動された場合、両方が同時にロックファイルの存在を確認し、両方が並行して実行される可能性が非常に高くなります。
代わりに行うべきことは、ロックを取得して続行するかどうかを決定するためにsingle atomic operationを使用することです。これには複数の方法があります。たとえば、Linuxを使用している場合は、flock
コマンドを使用してアドバイザリロックを実行できます。
Linux固有のflock
、under bash
を使用したくない場合は、次のようにします。
set -o noclobber
if ! exec 9>/$LOCKFILE
then
echo "lockfile already present" >&2
exit 1
fi
...
echo "Process started at ..." >&9
これは単一のアトミック操作を実行します(ロックファイルを開きます)。 noclobber
オプションは、ファイルがすでに作成されている場合にexec
が失敗することを確認します。開いているファイル記述子に書き込むことにより、後でファイルに追加できます。
また、人間が使用するための診断メッセージを書き込む場合は、stdoutではなくstderr
に書き込むことをお勧めします。たとえば、
echo "This is an error message" >&2
ディレクトリに沿って移動し、一致するすべてのアイテムを順番にそれぞれに移動します
for item in *
do
[[ -d "$item" ]] && mv -f *."$item" "$item" 2>/dev/null
done
mv
からのエラー出力は破棄されるため、存在しないファイルを移動しようとする不正な試みは報告されません。
または、各ファイルを試し、対応するディレクトリに移動することもできます
for item in *
do
if [[ -f "$item" ]]
then
ext="${item##*.}"
[[ -d "$ext" ]] && mv -f "$item" "$ext"
fi
done
基本的に、2つの(接続された)値が必要です。パターン<-> Dest dir。
例えば "*.jpg"
<-> ~/Downloads/JPG
これは、連想配列と同じ構造です。
したがって、すべてのパターン宛先を1つの配列(ksh、bash、zsh)にリストできます。
unset filetype; declare -A filetype
filetype["*.pdf"]="$dir/PDF"
filetype["*.opm"]="$dir/OPM"
filetype["*.yml"]="$dir/YML"
filetype["*.css"]="$dir/CSS"
filetype["*.tar.gz"]="$dir/archives"
filetype["*.Zip"]="$dir/archives"
filetype["*.jpg"]="$dir/Pictures"
filetype["*.png"]="$dir/Pictures"
filetype["*.tiff"]="$dir/Pictures"
filetype["*.pm"]="$dir/Perl"
filetype["*.xls*"]="$dir/Excel"
filetype["*.doc*"]="$dir/Word"
そしてループは次のように減少します:
## Move files to various subfolders based on extensions
for ftype in "${!filetype[@]}" # list of array **keys**
do
find "$dir" -maxdepth 1 -name "$ftype" -exec mv {} "${filetype[$ftype]}" \;
done
スクリプト全体は次のようになります。
#!/bin/bash
LOCKFILE=/tmp/.hiddensync.lock
if [ -e "$LOCKFILE" ]
then
echo "Lockfile exists, process currently running."
echo "If no processes exist, remove $LOCKFILE to clear."
echo "Exiting..."
exit
fi
timestamp=$(date +%Y-%m-%d::%H:%M:%s)
echo "Process started at: $timestamp" > "$LOCKFILE"
dir=~/Downloads
unset filetype; typeset -A filetype
filetype["*.pdf"]="$dir/PDF"
filetype["*.opm"]="$dir/OPM"
filetype["*.yml"]="$dir/YML"
filetype["*.css"]="$dir/CSS"
filetype["*.tar.gz"]="$dir/archives"
filetype["*.Zip"]="$dir/archives"
filetype["*.jpg"]="$dir/Pictures"
filetype["*.png"]="$dir/Pictures"
filetype["*.tiff"]="$dir/Pictures"
filetype["*.pm"]="$dir/Perl"
filetype["*.xls*"]="$dir/Excel"
filetype["*.doc*"]="$dir/Word"
## Move files to various subfolders based on extensions
for ftype in "${!filetype[@]}"
do
find "$dir" -maxdepth 1 -name "$ftype" -exec mv {} "${filetype[$ftype]}" \;
done
echo "Task Finished, removing lock file now at $(date +%Y-%m-%d::%H:%M:%s)"
rm "$LOCKFILE"