У меня есть список наборов файлов с большим количеством расширений, но уникальными именами.
filename-1.foo.001
...
filename-1.foo.020
filename-1.foo.baz
filename-1.foo.bar1-2.baz
...
filename-1.foo.bar7-8.baz
another_filename.foo.001
another_filename.foo.002
...
another_filename.foo.009
another_filename.foo.baz
another_filename.foo.bar1-2.baz
another_filename.foo.bar3-4.baz
another_filename.foo.bar4-5.baz
another_filename.foo.bar7-8.baz
yet.a.different.file.name.foo.001
yet.a.different.file.name.foo.002
...
yet.a.different.file.name.foo.287
yet.a.different.file.name.foo.baz
yet.a.different.file.name.foo.bar1-2.baz
yet.a.different.file.name.foo.bar3-4.baz
yet.a.different.file.name.foo.bar4-5.baz
yet.a.different.file.name.foo.bar7-8.baz
moreFileNaming.foo.001
...
moreFileNaming.foo.009
moreFileNaming.foo.baz
moreFileNaming.foo.bar1-2.baz
moreFileNaming.foo.bar3-4.baz
moreFileNaming.foo.bar4-5.baz
moreFileNaming.foo.bar7-8.baz
Я хотел бы переименовать их, используя вывод, openssl rand -hex 8
чтобы получить случайное имя файла для каждого набора, например так:
9874f7187c914ea9.foo.001
...
9874f7187c914ea9.foo.020
9874f7187c914ea9.foo.baz
9874f7187c914ea9.foo.bar1-2.baz
...
9874f7187c914ea9.foo.bar7-8.baz
2f54a0b6528e3927.foo.001
2f54a0b6528e3927.foo.002
...
2f54a0b6528e3927.foo.009
2f54a0b6528e3927.foo.baz
2f54a0b6528e3927.foo.bar1-2.baz
2f54a0b6528e3927.foo.bar3-4.baz
2f54a0b6528e3927.foo.bar4-5.baz
2f54a0b6528e3927.foo.bar7-8.baz
71ad0aa90148b2f5.foo.001
71ad0aa90148b2f5.foo.002
...
71ad0aa90148b2f5.foo.287
71ad0aa90148b2f5.foo.baz
71ad0aa90148b2f5.foo.bar1-2.baz
71ad0aa90148b2f5.foo.bar3-4.baz
71ad0aa90148b2f5.foo.bar4-5.baz
71ad0aa90148b2f5.foo.bar7-8.baz
3721323156e921b5.foo.001
...
3721323156e921b5.foo.009
3721323156e921b5.foo.baz
3721323156e921b5.foo.bar1-2.baz
3721323156e921b5.foo.bar3-4.baz
3721323156e921b5.foo.bar4-5.baz
3721323156e921b5.foo.bar7-8.baz
Я пробовал for name (*.(<->|baz|bar<->.baz) result=$(openssl rand -hex 16) && mv $name $result
(это может быть неработоспособно, так как это было сделано несколько итераций назад), но когда это сработало, то далокаждый файлслучайное имя, я просто хочу, чтобы каждый набор оставался с тем же именем, просто случайным и с тем же размером. Sha1sum или любой другой метод тоже подойдет.
Как мне это сделать? Особенно для файлов.foo.bar-*.баз ?
Если мы сбросим фу
3721323156e921b5.001
...
3721323156e921b5.009
3721323156e921b5.baz
3721323156e921b5.bar1-2.baz
3721323156e921b5.bar3-4.baz
3721323156e921b5.bar4-5.baz
3721323156e921b5.bar7-8.baz
Тоже было бы нормально. Еще несколько вопросов:
- как мне нацелиться от начала файла до .foo?
- как мне зациклить созданную переменную, например,
result=$(openssl rand -hex 8)
чтобы использовать ее при переименованиях, и только после завершения набора присвоить ее снова, чтобы зациклить ее, пока не будет выполнен следующий набор и т. д.?
Спасибо!
решение1
Эта проблема состоит из нескольких частей:
- Разложите каждое имя файла на базовую часть и расширения.
- Примените последовательное преобразование к базовой части каждого имени.
- Переименуйте файлы в соответствии с выбранным преобразованием базовой части, сохранив расширения.
1. Разложение имен файлов
Из ваших примеров имен не совсем ясно, что вы считаете базовой частью имени файла. Разделителем, очевидно, является точка, но в примере типа yet.a.different.file.name.foo.bar1-2.baz
, какая точка? Вы упоминаете попытку использования *.(<->|baz|bar<->.baz)
, которая не будет рассматривать foo
или bar1-2
как расширение. Твик, который допускает их как расширение, — это .(foo|<->|baz|bar<->(|-<->).baz)
. Затем вы можете разбить имя файла $f
следующим образом:
setopt extended_glob
base=${f%%(.(<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
В качестве альтернативы, если допустимо определить базу как все до и исключая первую .foo.
, то разложение будет проще:
base=${f%*.foo.*}; extensions=${f#$base}
2. Применение последовательного преобразования
Если вы хотите применить детерминированное преобразование, вы можете просто пересчитывать каждый раз. Например, вы можете получить псевдослучайный результат, взяв MAC имени с секретным ключом, используя один и тот же секретный ключ каждый раз.
secret=$(openssl rand -hex 32)
for … # Loop over the files as per (3.), set $base and $extensions as per (1.)
new_base=${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}
(Примечание: секретный ключ будет виден другим пользователям, если они запустят его ps
во время openssl
работы. Я предполагаю, что в вашем случае это не проблема, но будущим читателям следует об этом помнить.)
Если вы хотите применить рандомизированное преобразование, вам нужно запомнить, чему соответствует каждая база. Есть два способа сделать это:
- Вы можете сгруппировать файлы по их базе, а затем обрабатывать одну базу за раз.
- Вы можете обрабатывать файлы по одному, но помните, чему соответствует каждая база, и создавайте новое сопоставление только в том случае, если база еще не была просмотрена.
Второй способ проще, а первый не имеет особых преимуществ, поэтому я покажу только второй способ.
Построитьассоциативный массивсопоставление баз с новыми базами.
typeset -A mapping
mapping=()
for … # Loop over the files as per (3.), set $base and $extensions as per (1.)
if ((!$+mapping[$base])); then
mapping[$base]=$(openssl rand -hex 8)
fi
new_base=$mapping[$base]
3. Переименование файлов
Zsh поставляется с очень полезным инструментом для переименования файлов:zmv
. Преобразование, которое вы хотите сделать, достаточно сложное, чтобы zmv не делал его тривиальным: и декомпозиция имени файла, и преобразование требуют дополнительной работы. Даже в вашем случае zmv имеет некоторые незначительные преимущества. В частности, zmv выдаст ошибку, если возникнет конфликт (крайне маловероятно из-за случайных факторов, если только вы не используете более короткие длины). Однако из-за сложности преобразования имени использование zmv неудобно, и проще написать простой цикл.
Вот полный фрагмент, использующий случайные имена.
setopt extended_glob
typeset -A mapping
mapping=()
for f in *.(foo|<->|baz|bar<->(|-<->).baz); do
base=${f%%(.(foo|<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
if ((!$+mapping[$base])); then
mapping[$base]=$(openssl rand -hex 8)
fi
new_base=$mapping[$base]
mv -i -- $f $new_base$extensions
done
Вот полный фрагмент, использующий детерминированные имена для заданного значения $secret
.
setopt extended_glob
secret=$(openssl rand -hex 32)
for f in *.(foo|<->|baz|bar<->(|-<->).baz); do
base=${f%%(.(foo|<->|baz|bar<->(|-<->).baz))#}; extensions=${f#$base}
new_base=${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}
mv -i -- $f $new_base$extensions
done
А вот однострочный вариант, использующий zmv
для детерминированного случая, использующий первый .foo.
для обозначения конца базы. -w
Флаг помогает с разбивкой.
autoload zmv
secret=$(openssl rand -hex 32)
zmv -w '*.foo.*' '${"$(openssl dgst -sha256 -hmac $secret <<<$1)"[-16,-1]}.foo.$2'
Использование zmv в рандомизированном случае сложнее, поскольку нам нужно сохранить информацию от одного шага преобразования до следующего. Мы можем просто упаковать некоторый код в подстановку команды, zmv … '$(…; if …; then mapping[$base]=…; …)'
поскольку назначение mapping
будет внутри подоболочки подстановки команды и, следовательно, будет иметь эффект только внутри подоболочки. Однако мы можем использовать условное назначение параметра${name=word}
, чтобы установить mapping[$base]
только если он не установлен.
typeset -A mapping; mapping=()
zmv -w '*.foo.*' '${mapping[${1}]=$(openssl rand -hex 16)}.foo.$2'
Использование zmv с разложением, которое не использует преимущества .foo
, как в более сложном примере в (1.) выше, приводит к гораздо более сложному коду. Просто ради примера, вот вызов zmv для детерминированного случая, использующий base
как временную переменную для хранения базового имени. Он использует ${name::=word}
для назначения переменной во время расширения параметра и ${…}[0]
для подавления этой части из расширения ( [0]
берет подстроку с 0-го символа, которая не существует, поскольку zsh начинает нумеровать элементы массива и символы строки с 1; что-то вроде [2,1]
также подойдет).
zmv '*.(<->|baz|bar<->.baz)' '${${base::=${f%%(.(<->|baz|bar<->(|-<->).baz))#}}[0]}${"$(openssl dgst -sha256 -hmac $secret <<<$base)"[-16,-1]}.${f#$base}'
решение2
Вы можете сделать что-то вроде:
autoload -Uz zmv # best in ~/.zshrc
typeset -A rand
zmv '(*).foo(.*)' '${rand[$1]=$(openssl rand -hex 8)}$2'
или '(*)(.foo.*)'
не ронять .foo
.
Для начала тестирования добавьте -n
опцию (пробный запуск) в zmv
.
zmv
инструмент пакетного переименования, реализованный как автозагружаемая функция.
Первый аргумент — это расширенный шаблон глобуса, а второй аргумент — строка, которая подвергается расширению слов, определяющему, как удаляются файлы с помощью $1
, $2
..., ссылающихся на соответствующие (...)
s в шаблоне.
${rand[$1]=$(cmd)}
выше задает элемент ассоциативного массива дляключто, что осталось от самого правого .foo.
выхода, cmd
если ранее было не установлено, что гарантирует, что вы всегда получите одно и то же значение заданногоключ.