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