Преобразовать только части имени файла в верхний регистр

Преобразовать только части имени файла в верхний регистр

В программе скрипта оболочки мне нужно преобразовать имена файлов в верхний регистр, если преобразованное имя файла еще не существует. В этом конкретном случае мне нужно изменить только базовое имя на верхний регистр, оставив расширение (если есть) как есть.

Моя идея решения этой проблемы состоит в том, чтобы сначала извлечь базовое имя и расширение по отдельности, преобразовать базовое имя в верхний регистр с помощью trкоманды, а затем проверить, существует ли измененное базовое имя вместе с расширением в каталоге или нет.

Если его нет, то я изменю исходное имя файла на заглавное базовое имя с помощью mv. Теперь я думаю, что это можно сделать двумя способами: во-первых, используя exprи, во-вторых, используя cutс .(пробел-точка-пробел) в качестве разделителя.

Если я хочу использовать exprдля извлечения базового имени (например, из имени файла - python1.pyили phonelist), то я написал это:

basefile=`expr "$filename" : '\(.*\)\.*.*' ` 

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

Может кто-нибудь объяснить, где я не прав. Также, пожалуйста, предложите, как можно использовать, exprчтобы извлечь только расширение из имени файла.

решение1

Если оболочка — bash, то используется только расширение параметров bash:

file="aaa.bbb.dat"

name=${file%.*} # delete everything after last dot 
ext=${file##*.} # delete everything up to last dot
upcase=${name^^*}.$ext # uppercase everything

echo "$upcase"
AAA.BBB.dat

Попробуем рассмотреть более сложный случай:

file="déjà vu . dat "
name=${file%.*} # delete everything after last dot 
ext=${file##*.} # delete everything up to last dot
upcase=${name^^*}.$ext # uppercase everything
echo ":$upcase:"

Дает:

:DÉJÀ VU . dat :

Так:

  • Двойные кавычки не нужны, пока не будет использован результат
  • Заглавные буквы кажутся подходящими даже для символов, не входящих в набор ASCII

решение2

Когда есть неоднозначность в том, насколько далеко простирается группа, движки регулярных выражений отдают предпочтение самому длинному совпадению. Для любого имени файла \(.*\)соответствует всему имени и \.*.*соответствует пустой строке.

Вам понадобятся два случая: с расширением или без него. Обратите внимание, что если имя файла начинается с ., это не начало расширения.

Я не понимаю, почему вы хотите использовать expr. Манипулировать параметрами оболочки проще.

При преобразовании в верхний регистр обратите внимание, что trреализация в Linux не поддерживает локали, отличные от ASCII. Она выполняет только манипуляции с байтами. Например, echo accentué | tr a-z A-Zрезультатом будет ACCENTUé, а не ACCENTUÉ. Вместо этого используйте инструмент, поддерживающий локаль, например awk. В bash можно использовать ${filename^^?}, но в sh это недоступно. Убедитесь, что ваш скрипт запущен в правильной локали для кодировки имен файлов.

Я предполагаю, что имя файла не содержит части каталога. Если она есть, сначала отделите ее.

case $filename in
  ?*.*) # There is an extension
    base="${filename%.*}"; ext=".${filename##*.}";;
  *) # No extension
    base="$filename"; ext="";;
esac
upcased_base="$(printf %s. %base | awk '$0 = toupper($0)')"
upcased="${upcased_base%.}$ext"

Конечный символ ., %s.который затем удаляется, $upcased_baseгарантирует, что скрипт правильно обрабатывает имена файлов с новой строкой непосредственно перед расширением. Без этого подстановка команды удалит конечные символы новой строки. Вам это не нужно, если вы уже убедились, что имена ваших файлов не содержат символов новой строки.

решение3

Вот awkрешение, полностью основанное на -, в котором вам нужно поместить следующую строку в ваш скрипт оболочки:

uppercasename="$(echo "$filename" | awk 'BEGIN{FS=OFS="."} NF==1{$1=toupper($1)} {for (i=1;i<NF;i++) $i=toupper($i)} 1')"

Это будет использовать. в качестве разделителя полейдля ввода и выводаи, если найдено только одно поле, преобразовать его в верхний регистр, а во всех остальных случаях преобразовать все поля, кроме последнего, в верхний регистр. Затем он выводит результат (это значение 1, которое является сокращенной записью для {print}).

Если вы используете bash, вы можете избавиться от вертикальной черты и указать это как

uppercasename="$(awk 'BEGIN{FS=OFS="."} NF==1{$1=toupper($1)} {for (i=1;i<NF;i++) $i=toupper($i)} 1' <<< "$filename")"

с использованием строки here-string.

Обратите внимание, что это сделано так, что в пограничном случае, когда имя файла заканчивается на ., например myfile.this.txt., , он будет воспринимать это как «пустой, но присутствующий суффикс» и преобразовывать его вMYFILE.THIS.TXT. . Кроме того, если имя файланачинаетсяс .и не имеющим другого расширения (как в .myfile), он сохранит его в нижнем регистре.

Связанный контент