Ответ 1
BACKUPDIR=$(ls -t /backups | head -1)
$(...)
оценивает оператор в подоболочке и возвращает результат.
Я хотел бы найти новый каталог в каталоге и сохранить результат в переменной в bash.
Что-то вроде этого:
ls -t /backups | head -1 > $BACKUPDIR
Может ли кто-нибудь помочь?
BACKUPDIR=$(ls -t /backups | head -1)
$(...)
оценивает оператор в подоболочке и возвращает результат.
Это простое решение, использующее только ls
:
BACKUPDIR=$(ls -td /backups/*/ | head -1)
-t
заказы по времени (последняя первая)-d
перечисляет только элементы из этой папки*/
перечислены только каталогиhead -1
возвращает первый элементЯ не знал о */
, пока не нашел Список только каталогов, использующих ls в bash: Экзамен.
В приведенном выше решении не учитываются такие вещи, как файлы, которые пишутся и удаляются из каталога, в результате чего возвращается верхний каталог вместо нового подкаталога.
Другая проблема заключается в том, что это решение предполагает, что каталог содержит только другие каталоги, а не записываемые файлы.
Скажем, я создаю файл с именем test.txt и снова запустил эту команду:
echo "test" > test.txt
ls -t /backups | head -1
test.txt
В результате появляется test.txt вместо последнего измененного каталога.
Предлагаемое решение "работает", но только в лучшем случае.
Предполагая, что у вас максимум 1 глубина каталогов, лучше использовать следующее:
find /backups/* -type d -prune -exec ls -d {} \; |tail -1
Просто замените часть "/backups/" для вашего фактического пути.
Если вы хотите избежать абсолютного пути в bash script, вы всегда можете использовать что-то вроде этого:
LOCALPATH=/backups
DIRECTORY=$(cd $LOCALPATH; find * -type d -prune -exec ls -d {} \; |tail -1)
Чтобы получить новую папку с помощью ls -t
, возможно, вам придется различать файлы из папок, если в вашем каталоге нет только каталогов. Используя простой цикл, вы получите быстрый и быстрый результат, а также сможете легко реализовать различные фильтры в будущем:
while read i ; do if [ -d "${i}" ] ; then newestFolder="${i}" ; break ; fi ; done < <(ls -t)
Разработанный блок:
while read currentItemOnLoop # While reading each line of the file
do
if [ -d "${currentItemOnLoop}" ] # If the item is a folder
then
newestFolder="${currentItemOnLoop}" # Then save it into the "newestFolder" variable
break # and stop the loop
else
continue # Look for the next newest item
fi
done < <(ls -t) # Sending the result of "ls -t" as a "file" to the "while read" loop
Остерегайтесь логики continue
в моем разработанном блоке:
else
continue # Look for the next newest item
Вы не будете использовать его. Я разместил его только ради вашей видимости, так как в этом случае это не повлияет на результаты.
Ну, я думаю, что это решение является наиболее эффективным:
path="/my/dir/structure/*"
backupdir=$(find $path -type d -prune | tail -n 1)
Объяснение, почему это немного лучше:
Нам не нужны суб-оболочки (кроме одного для получения результата в переменной bash).
Нам не нужен бесполезный -exec ls -d
в конце команды find
, он уже распечатывает список каталогов.
Мы можем легко изменить это, например. чтобы исключить определенные шаблоны. Например, если вы хотите второй новейший каталог, потому что файлы резервных копий сначала записываются в каталог tmp по тому же пути:
backupdir=$(find $path -type -d -prune -not -name "*temp_dir" | tail -n 1)
Это чистое решение Bash:
topdir=/backups
BACKUPDIR=
# Handle subdirectories beginning with '.', and empty $topdir
shopt -s dotglob nullglob
for file in "$topdir"/* ; do
[[ -L $file || ! -d $file ]] && continue
[[ -z $BACKUPDIR || $file -nt $BACKUPDIR ]] && BACKUPDIR=$file
done
printf 'BACKUPDIR=%q\n' "$BACKUPDIR"
Он пропускает символические ссылки, включая символические ссылки на каталоги, которые могут или не могут быть правильными. Он пропускает другие не-каталоги. Он обрабатывает каталоги, имена которых содержат любые символы, включая символы новой строки и ведущие точки.