Как я могу улучшить этот скрипт, который сортирует файлы по расширению?

 man chmod 

...

-h Если файл является символической ссылкой, измените режим самой ссылки, а не файла, на который указывает ссылка.

...

Что-то вроде:

 chmod -h /Users/mego/opt/rel

должно работать.

2
16.01.2020, 22:46
5 ответов

При наличии нескольких расширений для пункта назначения вы можете добавить больше логики в директивы 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
27.01.2020, 21:49
  1. findможет выполнить команду mv самостоятельно, синтаксис для использования:

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

    Обратите внимание, что:{}будет заменено именем файла, и иногда вам нужно экранировать его таким образом\{\}

  2. Если вы терпимы к башизму, вы можете воспользоваться преимуществами массивов 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
27.01.2020, 21:49

На мой взгляд, я ненавижу дублировать код и склонен отделять данные от кода, поэтому у меня был бы способ определить отношение extension< ->target (здесь, в ключе -, проиндексированном массив, но это может быть файл конфигурации или база данных sqlite):

#! /bin/bash

declare -A destinations

destinations["jpg"]="images"
destinations["mp4"]="videos"
destinations["mov"]="videos"

shopt -s nullglob
for ext in "${!destinations[@]}"
do
    files=(*.$ext)
    [[ 0 -eq ${#files[*]} ]] && continue
    mv -t ${destinations[$ext]} "${files[@]}"
done

Это для bash. Shebang вашего скрипта использует sh, который иногда bash, а иногда что-то еще(dashв Ubuntu... ), поэтому, если код не является полностью тривиальным, я бы не стал использовать shв shebangs.

5
27.01.2020, 21:49

Прогуляйтесь по каталогам и переместите все подходящие предметы в каждый по очереди

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
27.01.2020, 21:49

В сущности, вам нужны два (соединенных )значения :Шаблон < --> Dest dir.

Например, "*.jpg"< -->~/Downloads/JPG

Это та же структура ассоциативного массива.

Итак, мы можем перечислить все шаблоны -dest в одном массиве (в 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
27.01.2020, 21:49

Теги

Похожие вопросы