Допустим, у меня есть папка с сотнями или тысячами файлов, все из которых названы по следующей схеме:
<random number of variable length>_<date code in YYYYMMDD format>.jpg
Пример:
73923_20180927.jpg
4457582_20180927.jpg
...
18733557_20190401.jpg
23573_20190401.jpg
...
Я ожидаю, что мой bash-скрипт выведет список этих кодов дат, т.е.
20180927
20190401
...
Это кажется более простой задачей, чем она есть на самом деле. Поскольку схема всегда одна и та же, я уже добился применения манипуляции строками, чтобы вывести только требуемую часть имен файлов. Однако я все еще пытаюсь понять, как вывести каждую дату только один раз.
Есть ли из этого какой-нибудь изящный выход?
решение1
Предположим, что все имена файлов соответствуют шаблону ./*_*.jpg
:
for name in ./*_*.jpg; do
name=${name##*_} # 4457582_20180927.jpg --> 20180927.jpg
printf '%s\n' "${name%.jpg}" # 20180927.jpg --> 20180927
done | sort -u
Это перебирает все имена. Для каждого имени он затем удаляет самую длинную строку префикса, соответствующую *_
. Затем он выводит оставшуюся строку с .jpg
удаленным суффиксом.
Затем все строки сортируются таким образом, что в конце выводится только список уникальных строк.
Если есть риск, что каталог может быть пустым, следует установить nullglob
опцию оболочки перед циклом ( shopt -s nullglob
). Это заставит цикл вообще не запускаться вместо того, чтобы запуститься один раз с нераскрытым шаблоном подстановки в $name
.
Без особой причины, вот как это сделать без sort
:
declare -A skip=()
for name in ./*_*.jpg; do
key=${name##*_} # 4457582_20180927.jpg --> 20180927.jpg
key=${key%.jpg} # 20180927.jpg --> 20180927
if [[ ! -v skip[$key] ]]; then
printf '%s\n' "$key"
skip[$key]=1
fi
done
Здесь я отслеживаю, какие строки уже были выведены как ключи в ассоциативном массиве. skip
Строка не будет выведена, если она соответствует ключу в массиве.
решение2
Предполагая, что на самом деле нет файлов с неправильными именами, запустите в этом каталоге:
ls -U | awk '-F[_.]' '{ print $2 }' | sort | uniq