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 {} +

🤔 А знаете ли вы, что...
Bash поддерживает передачу аргументов скрипту через командную строку.


82
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
  • пропускать короткие имена файлов
  • найти (необязательно) расширение
  • рассчитать максимальную длину головы
  • найти максимальную голову, которая заканчивается непробелами (имена файлов с недостаточным количеством пробелов пропускаются)
  • переименуй

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

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