man chmod
...
-h Если файл является символической ссылкой, измените режим самой ссылки, а не файла, на который указывает ссылка.
...
Что-то вроде:
chmod -h /Users/mego/opt/rel
должно работать.
При наличии нескольких расширений для пункта назначения вы можете добавить больше логики в директивы 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 и таким образом переписать свой скрипт:
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
На мой взгляд, я ненавижу дублировать код и склонен отделять данные от кода, поэтому у меня был бы способ определить отношение 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.
Прогуляйтесь по каталогам и переместите все подходящие предметы в каждый по очереди
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
В сущности, вам нужны два (соединенных )значения :Шаблон < --> 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"