web-dev-qa-db-ja.com

拡張子に従ってファイルをソートするこのスクリプトをどのように改善できますか?

小さなスクリプトがあり、それは単に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
2
Sono

宛先に複数の拡張子がある場合、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*

ノート:

  • nullglobがない場合、パターンに一致するファイルがない場合、関数はパターンを文字列として受け取ります。
    • たとえば、pdfファイルがない場合、シェルはmovefiles PDF/ "*.pdf"を実行します
  • nullglobで一致がない場合、シェルはコマンドからパターンを削除します:movefiles PDF/
  • これが、引数の数をチェックする理由です。一致するファイルがない場合、シフト後、$#はゼロであり、移動するものはありません。
8
glenn jackman
  1. findはmvコマンドを単独で実行できます。使用する構文は次のとおりです。

    find <filter> -exec mv {} <destination>

    ご了承ください: {}はファイル名に置き換えられ、そのようにエスケープする必要がある場合があります\{\}

  2. 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
3
binarym

タスクはべき等なので、この場合はそれほど問題になりませんが、ロックが正しくありませんであり、競合状態の影響を受けます。

ロックファイルの存在を確認し、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
2
b0fh

ディレクトリに沿って移動し、一致するすべてのアイテムを順番にそれぞれに移動します

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
0
roaima

基本的に、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"
0
Isaac