Как-то раз, мне понадобился скрипт который бы сам выбирал рандомные файлы из директории. Собственно, я написал его и сейчас выложу здесь.
На для начала разберемся как будем это делать. Итак, на входе подается список директорий, в которых лежат нужные нам файлы. Собираем файлы и пихаем в массив. Перемешиваем все файлы в массиве (тогда при последовательном обходе они станут случайными). И все. Очень просто. На самом деле мой скрипт немного сложнее, но весь алгоритм такой.
Вот код:
shuf -e ${filesArray[@]}
Проблема в том, что shuf не входит в POSIX.
Еще можно попробовать делать это через sort -R.
Вот и все.
На для начала разберемся как будем это делать. Итак, на входе подается список директорий, в которых лежат нужные нам файлы. Собираем файлы и пихаем в массив. Перемешиваем все файлы в массиве (тогда при последовательном обходе они станут случайными). И все. Очень просто. На самом деле мой скрипт немного сложнее, но весь алгоритм такой.
Вот код:
#!/bin/bash
onSIGINT() { # создаем функцию которая будет завершать вечный цикл
echo "SIGINT received, bye bye!"
exit 0
}
trap 'onSIGINT' INT # трап, вызывающий эту функцию на SIGINT
regexType="posix-egrep" # переменная указывающая на то, какие регулярные выражения будет использовать утилита find
regex=".*\.(mp3|flac)$" # сам шаблон регулярного выражения
depth="u" # глубина поиска. u [unlimited] - не ограничена, может быть числом
if [[ $# -eq 0 ]]; then # проверка. Если не получили аргументов, то выводим хелп и выходим
echo "Usage: $0 [-d DEPTH] DIR1 [DIR2 [DIR3 [...]]]"
exit 0
fi
index=0
for i in `seq 1 $#`; do # цикл обработки аргументов командной строки
arg=${!i}
if [[ $bNextDepth -eq 1 ]]; then
bNextDepth=0
if [[ $arg =~ ^[0-9]+$ ]]; then # по сути проверка - число ли depth или нет
depth=$arg
continue
else
echo "Missing value for -d." # ошибка. Юзер написал -d но не написал значение для него
exit 1
fi
fi
if [ "$arg" = "-d" ]; then
bNextDepth=1
if [[ $# -eq $i ]]; then
echo "Missing value for -d."
exit 1
fi
continue
fi
if [ ! -d "$arg" ]; then # проверяем, действительно ли нам подали директорию?
echo "Directory $arg does not exists."
else
dirList[$index]=$arg # сохраняем ее в массив
let "index++"
fi
done # конец обработки аргументов командной строки
if [[ ${#dirList[@]} -eq 0 ]]; then # если юзер глупый и не ввел не одну правильную директорию выходим, сообщая ошибку
echo "No one correct directory has been found."
exit 2
fi
index=0
if [ "$depth" != "u" ]; then # если у нас в depth число
depthArg="-maxdepth $depth"
fi
while read line
do
filesArray[$index]="$line"
let "index++"
done < <(find "${dirList[@]}" $depthArg -regextype "$regexType" -iregex "$regex" -type f) # когда я впервые увидел эту строку в одном примере, желание писать скрипты на баше у меня почти отпало. Спасибо людям на канале #gentoo-ru которые мне объяснили ее смысл. А про find я расскажу ниже.
filesNum=${#filesArray[@]}
echo "Found $filesNum files."
let "filesNum--" # уменьшаем на 1. Потому что for в баше (в отличии от Си) выберет последний элемент тоже.
while [ true ]; do
for i in `seq 0 "$filesNum"`; do # цикл который перемешает массив
let "rndPos = RANDOM % $filesNum"
temp="${filesArray[$i]}" # в этих 3 строках меняем местами элементы
filesArray[$i]="${filesArray[$rndPos]}"
filesArray[$rndPos]="$temp"
done
for i in `seq 0 "$filesNum"`; do # собственно, цикл запускающий нашу программу с нужным аргументом
/usr/bin/mplayer "${filesArray[$i]}"
done
done
exit 0 # всеfind "${dirList[@]}" $depthArg -regextype "$regexType" -iregex "$regex" -type f
-maxdepth N это параметр указывающий find'у, сколько раз надо проходить во вложенные директории (1 - ни разу не переходить).
Кстати, вместо блока кода
- Здесь ${dirList[@]} - массив корректных директорий который мы составили ранее.
- $depthArg (без кавычек) - два слова, первое - аргумент (-maxdepth), второе - его значение.
- $regexType - тип регулярного выражения.
- $regex - шаблон регулярного выражения по которому мы выбираем файлы из директории.
- -type f - указывает find'у то, что нас интересуют только реальные файлы.
-maxdepth N это параметр указывающий find'у, сколько раз надо проходить во вложенные директории (1 - ни разу не переходить).
Кстати, вместо блока кода
for i in `seq 0 "$filesNum"`; do
let "rndPos = RANDOM % $filesNum"
temp="${filesArray[$i]}"
filesArray[$i]="${filesArray[$rndPos]}"
filesArray[$rndPos]="$temp"
doneможно использовать утилиту shuf. То есть весь этот блок кода заменить на строку:shuf -e ${filesArray[@]}
Проблема в том, что shuf не входит в POSIX.
Еще можно попробовать делать это через sort -R.
Вот и все.
вот тебе задачка покруче: скрипт чтоб делал каталог музыки, музыка разбита на категории и подкатегории и под-подкатегории, количество уровней - разное, надо сделать папку с линками на все групы, но не альбомы
ОтветитьУдалитьТы молодец, чувак! Жаль, твой блог мне не попался, когда мы Linux в универе проходили.
ОтветитьУдалитьНу просто восхитительно!
ОтветитьУдалитьОчень хороший блог .. ;)) Я люблю его, потому что вы можете прийти к знаю, что многие вещи; ** поцелуев отправить польских **;
ОтветитьУдалить.......... (░)(░)
........(░)(♥)(░)
..........(░)(░)
............(.|./)
....(▒)(▒)..|/)(░)(░)
..(▒)(♥)(▒).(░)(♥)(░)
.....(▒)(▒)..|.(░)(░)
..................|/)
.............¯¯¯¯¯/
............. ) ¯¯¯(
............./▫◊ ◊▫
............/▫▫▫▫▫▫▫▫
.........../▫▪▫▪▫▪▫▪▫▫
.......... \_______/
Самый полезный блог из всех, которые я встречал. Пиши еще.
ОтветитьУдалитьСпасибо, пригодится.
ОтветитьУдалитьНу хоть где-то все доступно объясняют!
ОтветитьУдалитьзаставил меня почуствовать блондинкой
ОтветитьУдалитьБаш - полезный язык
ОтветитьУдалитьах ты ж шикарный =)
ОтветитьУдалитьгдеты раньше был?)))
давно искал нечто подобное (^_^)
ОтветитьУдалитьХуйня какая-то. Выясняем количество файлов, генерируем число, берем файл с индексом равным этому числу, все!
ОтветитьУдалитьСтрок пять.
Надо будет поставить на ноутбук иксось.
ОтветитьУдалитьLaserJet, и будут у нас случаи когда одни и те же файлы проходят чаще чем другие. Кроме того, если в моем скрипте убрать все проверки и фичу с заданием глубины аргументом, то будет тоже 5 строк.
ОтветитьУдалитьЭтот комментарий был удален администратором блога.
ОтветитьУдалитьЭтот комментарий был удален администратором блога.
ОтветитьУдалить