Bash-скрипт для сокращения имени файла до длины не более n, но без обрезки целых слов

У меня есть сценарий 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 {} +

Я бы для каждого имени файла создал массив всех слов, а затем просмотрел его, суммируя общую длину. Как только вы превысите предельную длину, отбросьте этот элемент массива и все последующие и создайте исходное имя файла. Для облегчения отладки я настоятельно рекомендую написать сценарий оболочки, который выполняет эту операцию для одного файла, а затем вызывать сценарий, например, find. Кстати, вы не указали, как поступать с именами файлов, в имени которых есть символ новой строки, а также с именами файлов, в имени которых есть два последовательных пробела.

user1934428 27.08.2024 09:14

Каким должно быть новое имя, если в нем только одно слово длиннее n, например foobar.txt с n=6? Хотите переименовать как .txt, fo.txt или не переименовывать вообще? А что, если два новых имени одинаковы?

Renaud Pacalet 27.08.2024 14:57

@RenaudPacalet в моем случае имя будет ограничено 120 символами, и вероятность возникновения таких сценариев равна нулю.

fuzzybabybunny 27.08.2024 16:07

Хороший. Я предлагаю вам добавить в свой вопрос эту важную информацию: 1. все имена файлов содержат пробелы слева от позиции обрезки, 2. гарантированно, что новые имена файлов будут разными.

Renaud Pacalet 27.08.2024 16:13
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
82
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вы были очень близки. Попробуйте это:

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}, возможно.

Renaud Pacalet 27.08.2024 13:05

И даже с .{0,...} это также обрежет небольшие имена файлов: с n=120 и f = "./a a.txt" выводится mv -- ./a a.txt ./a.txt.

Renaud Pacalet 27.08.2024 15:52

@RenaudPacalet хммм, видимо мне нужен кофе

jhnc 27.08.2024 18:13

Попробуйте упростить задачу и найти файлы с помощью 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")

Это предполагает, что имена ваших файлов не содержат символов новой строки.

Другие вопросы по теме

Похожие вопросы

Как извлечь часть строки с помощью sed?
Как использовать регулярное выражение для извлечения набора определенных подстрок?
Негативный просмотр, похоже, не работает должным образом
Объединить многострочные строки в фрейме данных pandas на основе шаблона регулярного выражения
Регулярное выражение для текста, разделенного запятыми, с необязательными двойными кавычками, которые могут содержать кавычки, экранированные обратной косой чертой
Почему эта замена регулярного выражения с использованием захвата зависает в этом быстром коде?
Токенизация XSLT с помощью регулярного выражения для токенизации только в том случае, если за точкой с запятой не следует пробел и число
Regex соответствует определенным шаблонам URL-адресов
Используйте одно регулярное выражение для извлечения информации из двух шаблонов
Регулярное выражение для соответствия начальной нумерации или алфавитным маркерам, например (a)