Удаление всех папок, которые не связаны в каталоге с bash
У меня есть задача, которая требует от меня удалить все папки в каталоге, которые не являются символическими ссылками, сохраняя только папки с символическими ссылками и символические ссылки.
Если у меня есть, например, 3 ресурса: ссылка, связанная папка и несвязанная папка. ссылка и связанная папка будут сохранены.
В настоящее время мне удалось получить этот код, который работает для имеющейся у нас структуры имен файлов, но я хотел бы знать, есть ли более чистое решение?
protected_folders=`ls -l | awk '/^l/ {printf $10}' | tr '/' '|'`;
symlinks=`ls -l | awk '/^l/ {print $8}' | tr "\n" "|"`;
protected_resources=$protected_folders$symlinks;
protected_resources=${protected_resources:0:${#protected_resources}-1};
folders_to_delete=`ls | grep -v -E "$protected_resources"`;
echo $folders_to_delete | tr '\n' ' ' | tr ' ' '\000' | xargs -0 rm -rf
Я знаю, что приведенная выше команда может быть выполнена в одной строке, но я разделил ее, так как меня попросили сделать ее более "читабельной".
Любая помощь будет оценена.
4 ответа
Может быть проще переместить символические ссылки и их цели в новый каталог, а затем заменить старый каталог новым.
ВНИМАНИЕ: это просто набросок, чтобы дать вам идею. [ОБНОВЛЕНИЕ: очистить и сделать его менее похожим на псевдокод]
# Make an empty directory to hold the symlinks and targets
# that you want to keep
DIR_TO_CLEAN=/the/directory/to/clean/up
TMP_DIR=$(mktemp -d)
for x in $DIR_TO_CLEAN/*; do
# Move each symlink and its target to the new directory
[[ -h $x ]] && mv $x $(readlink $x) $TMP_DIR
done
# FIRST!!!, confirm that $TMP_DIR has everything you want to keep,
# and $DIR_TO_CLEAN has nothing you want to keep
rm -rf $DIR_TO_CLEAN
mv $TMP_DIR $DIR_TO_CLEAN
Я не знаю, назовешь ли ты это чище, или элегантно, или нелепо сложно. Это много настроек, но последние несколько строк "реальной работы" достаточно ясны.
# map - apply a function to all arguments
map() {
fn="$1"
shift
for x in "$@"; do
$fn $x
done
}
echoif() { if $1 "$2" ; then echo $2 ; fi }
# use map/echoif to find items that meet a criteria in a list
filter() {
op=$1
shift
map "echoif $op" "$@"
}
# list grep, essentially
find_in_list() {
item=$1
shift
itemis() { test $item == $1 ; }
res=`filter itemis "$@"`
test -n "$res"
}
not() { if $* ; then return 1; fi ; }
not_in_list() { not find_in_list $* ; }
# utility functions for filtering
islink() { test -L $1 ; }
isdir() { test -d $1 ; }
# actual work starts here
links=`filter islink *`
dests=`map readlink $links`
should_delete() { not_in_list $1 $links $dests ; }
dirs=`filter isdir *`
todelete=`filter should_delete $dirs`
# you know what to do here
echo rm -rf $todelete
Вы можете найти все каталоги, которые хотите сохранить, следующим образом:
keep_me=$(for link in $(find . -type l); do find -L . -samefile $link; done)
Вы можете объединить это с решением @chepner следующим образом:
mkdir ../new_tmp_dir
for link in $(find . -type l)
do
find -L . -samefile $link -print0
done | xargs -0 -I{} mv {} ../new_tmp_dir
rm -rf *
mv ../new_tmp_dir/* .
Возможно, вам придется изменить операторы поиска (используя -maxdepth=1
) если каталоги, которые вы перемещаете, имеют глубину более одного уровня.
Вот одно из возможных решений, просто запустите
mkdir "./onlylinkedfiles" && cd "./onlylinkedfiles"
tar -ch "../symlinks" | tar -x --strip-components=1
а у тебя есть только и все файлы и директы по ссылкам ../symlinks/
в текущем каталоге.
То же самое с некоторой автоматизацией:
cd "delete_here"
f="../symlinkshere"; mkdir ../temporary && pushd ../temporary && tar -vch "${f}" | tar -x --strip-components=1 && rm -rf "`dirs +1`" && cd .. && mv temporary "`dirs +1`" && popd
После этого текущий каталог содержит только файлы, которые получили символические ссылки на ../symlinkshere/
,
Давайте разберемся с этим:
# Specify directory where symlinks are:
f="../symlinkshere";
# Create temporary directory:
mkdir ../temporary &&
# Remember current and change cwd to temporary folder:
pushd ../temporary &&
# Copy all symlinked files and directories to cwd:
tar -vch "${f}" | tar -x --strip-components=1 &&
# Remove directory where we been when pushd called:
rm -rf "`dirs +1`" &&
# Cd out of temporary dir:
cd .. &&
# Rename temporary to original cwd
mv temporary "`dirs +1`" &&
# Go back to path where we started:
popd
Вот сценарий оболочки для легкого использования:
Он работает в текущем каталоге и принимает один аргумент - путь к символическим ссылкам. cd /path/to/clean
replacebylinks.sh /here/is/symlinks/
ФАЙЛ: ~/bin/replacebylinks.sh
#!/bin/bash
tmp="`mktemp -d`"
sourced="`readlink -f ${1}`"
workd="`pwd -P`"
cd "${tmp}"
tar -vchC "${sourced}" . | tar -xv --strip-components=1
rm -vrf "${workd}"
cd ..
mv -v "${tmp}" "${workd}" && cd "${workd}"
Некоторые заметки:
- Подробный переключатель
-v
может быть удален из команд, если вы думаете, что слишком много выходных данных. - Может быть лучше использовать
tar -chC "source" .
как без-C source
все дерево каталогов источника сохраняется в месте назначения. С абсолютными путями это начинается с/
корень.