У меня есть сценарий bash, который просматривает папку и находит все файлы с именами файлов длиной более n и сокращает имя файла до n.
Я хочу, чтобы имя файла было сокращено до длины n, но в случае, если это приведет к обрезке слова, вместо обрезки слова просто полностью удалите это обрезанное слово и завершите новое имя файла последним символом, который не является космос.
Пример:
sample file name so long wrangling roose turns bridge.txt
Когда я запускаю скрипт, я могу получить:
sample file name so long wrangling ro.txt
Я бы хотел, чтобы это было:
sample file name so long wrangling.txt
Это мой текущий скрипт, который просто обрезает слова:
#!/bin/bash
export n=120 # length of filename desired
find . -type f \
! -name '.*' \
-regextype egrep \
! -regex '.*\.[^/.]{'"$n"',}' \
-regex '.*[^/]{'$((n+1))',}' \
-execdir bash -c '
for f in "${@#./}"; do
ext=${f#"${f%.*}"}
mv -- "$f" "${f:0:n-${#ext}}${ext}"
done' bash {} +
Каким должно быть новое имя, если в нем только одно слово длиннее n, например foobar.txt с n=6? Хотите переименовать как .txt, fo.txt или не переименовывать вообще? А что, если два новых имени одинаковы?
@RenaudPacalet в моем случае имя будет ограничено 120 символами, и вероятность возникновения таких сценариев равна нулю.
Хороший. Я предлагаю вам добавить в свой вопрос эту важную информацию: 1. все имена файлов содержат пробелы слева от позиции обрезки, 2. гарантированно, что новые имена файлов будут разными.





Вы были очень близки. Попробуйте это:
tmp = "${f%.*} "
tmp=${tmp::n-${#ext}+1}
mv -- "$f" "${tmp% *}$ext"
bash может выполнять сопоставление регулярных выражений напрямую:
export n=120
find . -type f ! -name '.*' -execdir bash -c '
declare -n m=BASH_REMATCH
for f; do
(( ${#f} > n )) || continue
[[ $f =~ \.[^\ ./]+$ ]]
(( max = n-1-${#m} ))
[[ $f =~ ^(\./.{0,$max}[^[:space:]])[[:space:]].*("$m")$ ]] &&
echo mv -- "$f" "${m[1]}${m[2]}"
done
' - {} +
BASH_REMATCHХорошо, но, боюсь, .{,n} не POSIX и, следовательно, непереносим. Например, он работает под GNU/Linux, но не под macOS. Замените на .{0,n}, возможно.
И даже с .{0,...} это также обрежет небольшие имена файлов: с n=120 и f = "./a a.txt" выводится mv -- ./a a.txt ./a.txt.
@RenaudPacalet хммм, видимо мне нужен кофе
Попробуйте упростить задачу и найти файлы с помощью find, а затем обрезать имена с помощью fold+head:
$ echo 'sample file name so long wrangling roose turns bridge.txt' | fold -s -w 40 | head -1
sample file name so long wrangling
затем добавьте .txt обратно, например. что-то вроде этого, непроверенное:
sfx='.txt'
lgth='120'
while IFS= read -r file; do
trunc=$(printf '%s\n' "${file%%$sfx}" | fold -s -w "$lgth" | head -1)
echo mv -- "$file" "${trunc}${sfx}"
done < <(find . -type f -name "*"$sfx")
Это предполагает, что имена ваших файлов не содержат символов новой строки.
Я бы для каждого имени файла создал массив всех слов, а затем просмотрел его, суммируя общую длину. Как только вы превысите предельную длину, отбросьте этот элемент массива и все последующие и создайте исходное имя файла. Для облегчения отладки я настоятельно рекомендую написать сценарий оболочки, который выполняет эту операцию для одного файла, а затем вызывать сценарий, например,
find. Кстати, вы не указали, как поступать с именами файлов, в имени которых есть символ новой строки, а также с именами файлов, в имени которых есть два последовательных пробела.