У меня есть сервер, который получает файл для каждого клиента каждый день в каталог. Имена файлов строятся следующим образом:
uuid_datestring_other-data
Например:
d6f60016-0011-49c4-8fca-e2b3496ad5a7_20160204_023-ERROR
uuid
это стандартный формат uuid.datestring
это выход изdate +%Y%m%d
.other-data
переменная по длине, но никогда не будет содержать подчеркивания.
У меня есть файл в формате:
#
d6f60016-0011-49c4-8fca-e2b3496ad5a7 client1
d5873483-5b98-4895-ab09-9891d80a13da client2
be0ed6a6-e73a-4f33-b755-47226ff22401 another_client
...
Мне нужно проверить, что каждый uuid, указанный в файле, имеет соответствующий файл в каталоге, используя bash.
Я дошел до этого, но чувствую, что я иду не в том направлении, используя оператор if, и мне нужно перебирать файлы в исходном каталоге.
Переменные source_directory и uuid_list были назначены ранее в скрипте:
# Check the entries in the file list
while read -r uuid name; do
# Ignore comment lines
[[ $uuid = \#* ]] && continue
if [[ -f "${source_directory}/${uuid}*" ]]
then
echo "File for ${name} has arrived"
else
echo "PANIC! - No File for ${name}"
fi
done < "${uuid_list}"
Как я должен проверить, что файлы в моем списке существуют в каталоге? Я хотел бы использовать функциональность bash, насколько это возможно, но я не против использования команд, если это необходимо.
command-line
bash
scripts
Arronical
источник
источник
Ответы:
Пройдите по файлам, создайте ассоциативный массив над uuids, содержащимися в их именах (я использовал расширение параметра для извлечения uuid). Прочитайте список, проверьте ассоциативный массив для каждого uuid и сообщите, был ли файл записан или нет.
источник
cd
в каталог в сценарии, но просто интересно, ради получения знаний.file=${file##*/}
.Вот более «застенчивый» и лаконичный подход:
Обратите внимание, что, хотя вышеприведенный пример хорош и будет хорошо работать для нескольких файлов, его скорость зависит от количества UUID и будет очень медленной, если вам потребуется обработать многие из них. Если это так, либо воспользуйтесь решением @ choroba, либо, для чего-то действительно быстрого, избегайте оболочки и вызывайте
perl
:Просто чтобы проиллюстрировать разницу во времени, я протестировал мой подход bash, choroba и мой perl к файлу с 20000 UUID, из которых 18001 имел соответствующее имя файла. Обратите внимание, что каждый тест выполнялся путем перенаправления вывода скрипта на
/dev/null
.Мой удар (~ 3,5 мин)
Чороба (удар, ~ 0,7 сек)
Мой Perl (~ 0,1 сек):
источник
cd
в каталог в сценарии, но есть ли способ, с помощью которого путь поиска файлов может быть включен в поиск?${source_directory}
же, как вы делали в вашем сценарии."$2"
и передайте его сценарию в качестве второго аргумента.Это чистый Bash (т.е. никаких внешних команд), и это наиболее совпадающий подход, который я могу придумать.
Но с точки зрения производительности на самом деле не намного лучше, чем у вас сейчас.
Он будет читать каждую строку из
path/to/file
; для каждой строки будет сохранено первое поле$uuid
и напечатано сообщение, если файл, соответствующий шаблонуpath/to/directory/$uuid*
, не найден:Назовите его
path/to/script path/to/file path/to/directory
.Пример вывода с использованием примера входного файла в вопросе в иерархии тестовых каталогов, содержащего пример файла в вопросе:
источник
Идея здесь не в том, чтобы беспокоиться о сообщениях об ошибках, которые оболочка сообщит вам. Если вы попытаетесь
<
открыть файл, который не существует, ваша оболочка будет жаловаться. Фактически, он добавит ваш скрипт$0
и номер строки, в которой произошла ошибка, к выводу ошибки, когда это произойдет ... Это хорошая информация, которая уже предоставлена по умолчанию - так что не беспокойтесь.Вам также не нужно переносить файл построчно - это может быть очень медленно. Это расширяет все в одном кадре до массива аргументов, разделенных пробелами, и обрабатывает два одновременно. Если ваши данные соответствуют вашему примеру, то
$1
всегда будет ваш uuid и$2
будет вашим$name
. Еслиbash
можно открыть совпадение с вашим uuid - и существует только одно такое совпадение - тогдаprintf
произойдет. В противном случае это не так, и оболочка пишет диагностику в stderr о том, почему.источник
unset IFS
обеспечивает$(cat <uuid_file)
разделение на пустое пространство. Оболочки разделяются по-$IFS
разному, когда они состоят только из пробелов или не заданы. Такие расщепленные расширения никогда не имеют нулевых полей, потому что все последовательности пробелов стоят как один разделитель полей. Я думаю, что если в каждой строке есть только два поля, не разделенных пробелами, это должно работать. воbash
всяком случае.set -f
гарантирует, что расширение без кавычек не будет интерпретировано для глобов, а set + f гарантирует, что более поздние глобусы будут.<>
потому что это создает несуществующий файл.<
сообщит, как я хотел. хотя возможная проблема с этим - и причина, по которой я неправильно использовал<>
в первую очередь - заключается в том, что если это файл канала без ридера или как строковый буфер char dev, он зависнет. этого можно избежать, обрабатывая вывод ошибок более явно и делая это[ -f "$dir/$1"* ]
. мы говорим об uuids здесь, и поэтому он никогда не должен расширяться до более чем одного файла. Хотя довольно приятно, как он сообщает о неудачных именах файлов в stderr.<>
все равно можно было бы использовать таким образом ...<>
лучше, если glob может расширяться до каталога, потому что на linux чтение / запись будет потерпеть неудачу и сказать - это каталог.bash
перенаправленный глобус будет приниматься только в том случае, если он соответствует только одному файлу. см.man bash
под перенаправлением.Я бы подошел так: сначала получить uuids из файла, а затем использовать
find
Для читабельности,
Пример со списком файлов в
/etc/
поиске имен файлов passwd, group, fstab и THISDOESNTEXIST.Поскольку вы упомянули, что каталог плоский, вы можете использовать
-printf "%f\n"
опцию, чтобы просто напечатать имя файлаТо, что это не делает, - перечисляет отсутствующие файлы
find
Небольшим недостатком является то, что он не сообщает вам, не находит ли он файл, только когда он совпадает с чем-то. Что можно сделать, однако, это проверить вывод - если вывод пуст, то у нас отсутствует файлБолее читабельно:
И вот как он работает как маленький скрипт:
Можно использовать в
stat
качестве альтернативы, поскольку это плоский каталог, но приведенный ниже код не будет работать рекурсивно для подкаталогов, если вы когда-нибудь решите добавить их:Если мы возьмем
stat
идею и продолжим с ней, мы могли бы использовать код выхода stat в качестве указания на то, существует файл или нет. По сути, мы хотим сделать это:Образец прогона:
источник