Converter apenas partes de um nome de arquivo em letras maiúsculas

Converter apenas partes de um nome de arquivo em letras maiúsculas

Em um programa de script de shell, preciso converter os nomes dos arquivos em letras maiúsculas se o nome do arquivo convertido ainda não existir. Neste caso específico preciso alterar apenas o nome base para maiúscula deixando a extensão (se houver) como está.

Minha idéia de resolver o problema é extrair primeiro o nome base e a extensão separadamente, converter o nome base em maiúsculas usando tro comando e então verificar se o nome base alterado junto com a extensão existe no diretório ou não.

Se não existir, alterarei o nome do arquivo original com o nome de base maiúsculo usando mv. Agora acho que isso pode ser feito de duas maneiras: primeiro usando expre depois usando cutwith .(space-period-space) como delimitador.

Se eu quiser exprextrair o nome base (por exemplo, do nome do arquivo - python1.pyou phonelist), escrevi isto:

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

Eu usei \.*também para aqueles nomes de arquivos que não possuem nenhuma extensão porque \.* ignorariam zero ou mais ocorrências de ., mas esta expressão para exprnão está funcionando corretamente. Para qualquer nome de arquivo, ele retorna o nome completo do arquivo como está.

Alguém pode explicar onde estou errado. Além disso, sugira como posso exprextrair apenas a extensão do nome do arquivo.

Responder1

Se o shell for bash, usando apenas a expansão dos parâmetros do 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

Tentando com um caso mais difícil:

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á:

:DÉJÀ VU . dat :

Então:

  • aspas duplas não são necessárias, até usar o resultado
  • Maiúsculas parecem boas mesmo para caracteres não-ASCII

Responder2

Quando há uma ambigüidade na extensão de um grupo, os mecanismos de regex favorecem primeiro a correspondência mais longa. Para qualquer nome de arquivo, \(.*\)corresponde ao nome completo e \.*.*à string vazia.

Você precisará de dois casos: com ou sem extensão. Observe também que se o nome de um arquivo começar com ., isso não será o início de uma extensão.

Não entendo por que você quer usar expr. A manipulação dos parâmetros do shell é mais fácil.

Ao converter para letras maiúsculas, observe que a trimplementação no Linux não suporta localidades não-ASCII. Ele faz apenas manipulação de bytes. Por exemplo, echo accentué | tr a-z A-Zresulta em ACCENTUé, não ACCENTUÉ. Use uma ferramenta com reconhecimento de localidade, como o awk. No bash, você pode usar ${filename^^?}, mas isso não está disponível no sh. Certifique-se de que seu script esteja sendo executado na localidade correta para a codificação dos nomes dos arquivos.

Presumo que o nome do arquivo não contenha uma parte do diretório. Se isso acontecer, separe-o primeiro.

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"

O final .que %s.é removido $upcased_basegarante que o script lide corretamente com os nomes dos arquivos com uma nova linha imediatamente antes da extensão. Sem isso, a substituição do comando eliminaria as novas linhas finais. Você não precisa disso se já tiver garantido que os nomes dos arquivos não contêm caracteres de nova linha.

Responder3

Aqui está uma awksolução inteiramente baseada em , onde você colocaria a seguinte linha em seu script de shell:

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

Isso usará o .separador de campopara entrada e saídae, se apenas um campo for encontrado, converta-o para letras maiúsculas e, em todos os outros casos, converta todos os campos, exceto os últimos, para letras maiúsculas. Em seguida, ele imprime o resultado (este é o significado de 1, que é uma notação abreviada para {print}).

Se você estiver usando bash, você pode se livrar do tubo e declará-lo como

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

usando uma string aqui.

Observe que isso foi projetado para que, no caso limítrofe de um nome de arquivo terminando em a ., como em myfile.this.txt., ele o trate como um "sufixo vazio, mas presente" e o converta em MYFILE.THIS.TXT.. Além disso, se o nome do arquivocomeçacom a .e não tem outra extensão (como em .myfile), será mantido em minúsculas.

informação relacionada