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
tr
o 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 expr
e depois usando cut
with .
(space-period-space) como delimitador.
Se eu quiser expr
extrair o nome base (por exemplo, do nome do arquivo - python1.py
ou 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 expr
nã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 expr
extrair 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 tr
implementaçã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-Z
resulta 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_base
garante 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 awk
soluçã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.