Получение регулярного выражения для соответствия имени файла


2

У меня есть скрипт, который перечисляет кучу файлов, которые соответствуют определенным критериям. Он выводит только имена файлов, и есть куча текста, который не нужен.

Пример строки:

[gg]_Magi_-_13_[DB38165F].mkv

Что я хотел бы достичь в выводе:

[gg]_Magi_-_13

Мне удалось заменить подчеркивание, но мне не повезло в успешной обрезке [CRC32] .mkv. Также я ограничиваю количество символов и помещаю многоточие в конец, если они выходят за пределы 28 символов, но даже если оно не выходит за пределы 28 символов, оно все равно добавляет многоточие в конец.
Код для этого:

print substr( $0, 0, 28 )"[…]"}

Помощь по любой из этих проблем будет высоко ценится.


Какой язык вы используете? Кроме того, все ли имена файлов в этом формате?
Aluísio ASG

@ Aluísio ASG - я передаю все это через bash-скрипт. Большинство, если не все имена файлов соответствуют этому формату.
user181353

@DW Вы когда-нибудь использовали файловый менеджер Ranger? Он позволяет вам переименовывать файлы, используя Vim (который, как вы, возможно, знаете, позволяет вам редактировать блоки по вертикали). Я считаю, что это действительно делает сложное переименование очень легким (обычно требуется всего несколько простых правок). github.com/hut/ranger

@OP Я только что заметил проблему с твоей проблемой. Если вы усечете все имена файлов до 28 символов с помощью многоточия, и у вас будет набор файлов длиной 32 символа, где счетчик превышает 28 символов, вы будете переименовывать файлы в одно и то же имя файла и в итоге получите один файл. Например, [blahblah] blahblahblahblahblah 01и [blahblah] blahblahblahblahblah 02оба становятся [blahblah] blahblahblahblahb...(больше не разные файлы)

Ответы:


1

Я думаю, что это самое короткое решение, которое отвечает всем критериям

awk '{
    if (match($0, "^(.*)_[^_]+$", a)) {
        print substr(a[1], 1, 27) (length(a[1]) > 27 ? "..." : "")
    }
}'

Две линии чистого bashтоже сделают это, что, возможно, еще короче, не говоря уже о том, чтобы быстрее. Смотри мой ответ .
копишке

1
sed -e 's/_\[.*\.mkv//' -e 's/^\(.\{28\}\).*/\1.../' file.txt

Первый бит удаляет _[blah].mkv, а второй бит печатает первые 28 символов и ставит ...в конце - но если строка меньше 28 символов, он печатает только удаленное имя файла без добавления эллипсов.

Если расширение файла не всегда будет * .mkv, вы можете использовать это (в sed $означает «до конца строки»):

sed -e 's/_\[.*$//' -e 's/^\(.\{28\}\).*/\1.../' file.txt

Хорошо, но я всегда нахожу sedнемного излишним для такой довольно простой манипуляции со строками. Смотрите мой ответ для двух строк чистого, bashкоторые достигают того же результата.
копишке

1

Хотя awk, sedи компания имеет свои достоинства, они не нужны для этого. Вы можете легко достичь всего, что просили, используя только bashстроковые операции и сопоставление с образцом. Предполагая, что вы присвоили свое имя файла $name:

name="${name%_\[*\].*}"

будет отрезать тип файла и заключенный в скобки CRC из $name. Если вам нужно убедиться на 100%, что вы отключили только CRC, вы можете использовать расширенное регулярное выражение вместо вышеперечисленного:

[[ $name =~ (.*)_\[[[:xdigit:]]{8}\]\..*$ ]] && name="${BASH_REMATCH[1]}"

Усечение имен длиннее 28 символов достигается:

(( ${#name} > 28 )) && name="${name::27}…"

- две строки bashитога (не считая логики, цикла или другого, чтобы получить имена ваших файлов в var и, конечно же, выходной код), без внешних. Основным преимуществом является то, что код работает быстро, поскольку оболочке никогда не требуется запускать какие-либо внешние двоичные файлы.


ОК, это действительно здорово. Определенно лучший ответ здесь.
evilsoup

0

Попробуйте эту функцию bash (предупреждение: не проверено):

function convert_filename {
    # Regex guide:
    #   ^(.*)_?                everything since the beginning of the string,
    #                          optionally followed by an underscore
    #   \[[a-fA-F0-9]{8}\]    8 hexadecimal characters, surrounded by []
    #   \.(.\w+)$              filename extension at the end of the string
    local r="$(echo "$1" | sed -r 's/^(.*)_?\[[a-fA-F0-9]{8}\]\.(.\w+)$/\1/')"
    if (( ${#r} < 28 )); then
        # Outputs $r
        echo "$r"
    else
        # Outputs the first 27 characters from $r followed by an ellipsis
        echo "${r::27}…"
    fi
}

0

Не самое чистое решение, но вы могли бы сделать это:

 echo "[gg]_Magi_-_13_[DB38165F].mkv" | awk -F '_' '{print $1"_"$2"_"$3"_"$4}'

РЕДАКТИРОВАТЬ: Мех, поцарапать этот ответ. Это не даст вам элипсис.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.