У меня есть сценарий 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
. Кстати, вы не указали, как поступать с именами файлов, в имени которых есть символ новой строки, а также с именами файлов, в имени которых есть два последовательных пробела.