Попросите grep
самому построить список файлов, рекурсивно из текущего каталога:
grep -r MyPattern.
Это не совсем то же самое, что и *
, так как он будет искать в подкаталогах -, но для почтовых каталогов это обычно то, что вам нужно.
Проблема с вашим кодом заключается в том, что вы -создаете список каждый раз, чтобы выбрать новый путь. Это потенциально может дать вам одни и те же пути снова и снова, пока вы храните одни и те же файлы в каталогах, для которых вы создаете список.
Простой ответ для случая, когда вы время от времени запускаете свой скрипт, состоит в том, чтобы переместить файлы процесса(или удалить их ). Таким образом, в следующий раз, когда вы запустите скрипт и повторно -создадите случайный список, уже обработанные файлы не будут частью списка.
Например, при условии, что все файлы расположены в каталоге $HOME/newfiles
или ниже него, следующий код выберет файл, а затем переместит его в$HOME/oldfiles
:
myfile=$( find "$HOME/newfiles" -type f -print0 | shuf -z -n 1 )
# use "$myfile" here
# later... move "$myfile" to somewhere else:
mv "$myfile" "$HOME/oldfiles"
Остальная часть этого ответа касается случая, когда вы хотите перебрать рандомизированные пути в одном и том же вызове скрипта.
Предполагая, что ваши файлы и каталоги не содержат встроенных символов новой строки, это показывает то, что предложил Джефф Шаллер в комментарии:
find./ -type f | shuf |
while IFS= read -r pathname; do
# do work with "$pathname"
done
Это дало бы вам случайные пути к обычным файлам в текущем каталоге или ниже, если, как я уже упоминал, ни один из путей в иерархии не содержал новых строк (, и в этом случае shuf
зашифровывал бы эти имена ).
Безопасным вариантом было бы скремблировать список нулевым -завершенным списком:
readarray -t -d '' pathnames < <( find. -type f -print0 | shuf -z )
for pathname in "${pathnames[@]}"; do
# use "$pathname" here
done
Этот пример (и следующий )адаптированы изhttps://unix.stackexchange.com/a/543188/116858
В оболочке zsh
вы могли бы сделать
for pathname in./**/*(.DNnoe['REPLY=$RANDOM'])
do
# use $pathname here
done
Это работает аналогично приведенному выше коду с той разницей, что, поскольку здесь используется глобус оболочки и не используются инструменты фильтрации текста, -ориентированного на строку -, новые строки в именах файлов не будут проблемой (, и вы не будете не нужно обходить nul -завершенные списки ).
Удобство выполнения этого в zsh
заключается в том, что вам не нужно вызывать какие-либо внешние инструменты.
Если я правильно понимаю вопрос, одна вещь, которую может сделать OP, — это перетасовать список в файл (или переменную, если в сценарии BASH
), а затем извлечь элементы из этого списка. Таким образом, OP не будет вызывать один и тот же файл дважды до конца полного списка.
Например,
find./ -type f | shuf > shuffled.txt
чтобы создать список в файле, а затем вызвать его через что-то вроде,
cat shuffled.txt | head -1 | tail -1
cat shuffled.txt | head -2 | tail -1
cat shuffled.txt | head -3 | tail -1
...
Или эквивалентная строка с sed
или awk
.
В качестве альтернативы, если все это помещается в скрипт BASH
, можно сделать что-то подобное:
for filename in $(find./ -type f | shuf)
do
echo ${filename}
... do something to ${filename}
done
Как насчет того, чтобы просто работать с inode
....
[[ ! -f seen ]] && touch seen && ls -i seen > seen
file=$(find. -type f -printf %i"\n" | sort | join -j 1 -v 1 - seen | shuf -n 1)
echo $file >> seen
sort -o seen seen
find -inum $file -exec cat {} \; #or whatever you want to do with the file
Не имеет значения, находится ли файл seen
в вашем пути поиска, и если это так, то просто добавьте свой собственный файл inode
к себе, чтобы он был скрыт.
Для одного сеанса проверки просто прокрутите список
[[ ! -f seen ]] && touch seen && ls -i seen > seen
sort -o seen seen
list=$(mktemp)
find. -type f -printf %i"\n" | sort | join -j 1 -v 1 - seen | shuf -o $list
while read file; do
echo $file >> seen
find -inum $file -exec sh -c 'echo -e "$1 contains....\n"; cat "$1"; echo -e "\n\n"' sh {} \;
sleep 1
done < $list
Примечание:Предполагается, что файлы не удаляются. Если они есть и inode
используются повторно, их придется удалить из seen
После обнаружения того, что sed
копирует и перезаписывает файлы и изменяет inode
для файла seen
, этот подход становится более сложным.... Решением проблемы удаления может быть использование ed
, а не sed
.
Чтобы удалить файлtouch wood
d="touch wood"; find. -iname "$d" -printf %i"\n%p\n" | while read i ; do read f; rm "$f" ;printf "%s\n" "/$i/d" wq | ed -s seen; done;