Eu estou tentando definir uma variável em um script sh para os últimos 3 caracteres do nome base de um arquivo (por nome base, quero dizer, sem o caminho e sem o sufixo). Consegui fazer isso, mas, por pura curiosidade, estou me perguntando se existe um comando mais curto e único que eu possa usar. Originalmente eu tinha uma linha awk
, mas era bastante longa. Atualmente, tenho esse script de duas linhas (assumindo que um nome de arquivo completo esteja $1
):
filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`
Por exemplo, "/path/to/somefile.txt" termina com "ile" em $lastpart
.
Posso de alguma forma combinar basename
o bit para separar o sufixo em um único comando e existe uma maneira de enviá-lo paratail
(ou qualquer outra coisa que eu possa usar) sem usar um pipe? O sufixo é desconhecido, portanto não posso basear como parâmetro basename
.
O objetivo principal não é tanto o mais curto possível, mas o mais legível possível. O contexto real de tudo isso é essa pergunta no Superusuário , onde estou tentando encontrar uma resposta razoavelmente simples.
fonte
file.one.two.three
? Você querile
outwo
?two
funcionaria; a extensão disso seria.three
eu acho.Respostas:
Esse é um trabalho típico para
expr
:Se você souber que os nomes de arquivos têm o formato esperado (contém um e apenas um ponto e pelo menos três caracteres antes do ponto), isso pode ser simplificado para:
Observe que o status de saída será diferente de zero se não houver correspondência, mas também se a parte correspondente for um número que resolva para 0. (como para
a000.txt
oua-00.txt
)Com
zsh
:(
:t
para cauda (nome da base),:r
para descanso (com extensão removida)).fonte
expr
é outro com o qual eu preciso me familiarizar. Eu realmente gosto daszsh
soluções em geral (eu estava lendo sobre seu suporte para substituições aninhadas no lado esquerdo de um${}
ontem e desejandosh
ter o mesmo), é apenas uma chatice que nem sempre esteja presente por padrão.expr
era POSIX. Certamente é. Raramente está embutido.Essa primeira remove os últimos três caracteres e
$var
depois remove$var
os resultados dessa remoção - que retorna os últimos três caracteres de$var
. Aqui estão alguns exemplos mais especificamente destinados a demonstrar como você pode fazer isso:Você não precisa espalhar tudo isso através de tantos comandos. Você pode compactar isso:
Combinar
$IFS
comset
os parâmetros ting shell também pode ser um meio muito eficaz de analisar e perfurar as variáveis do shell:Isso vai ficar só três personagens imediatamente anterior ao primeiro período após o último
/
em$path
. Se você deseja recuperar apenas os três primeiros caracteres imediatamente anteriores ao último.
em$path
(por exemplo, se houver a possibilidade de mais de um.
no nome do arquivo) :Nos dois casos, você pode fazer:
E...
... imprimirá o que segue
.
Se você não se importa de usar um programa externo, pode:
Se houver a chance de um
\n
caractere ewline no nome do arquivo (não aplicável às soluções de shell nativas - todos eles lidam com isso de qualquer maneira) :fonte
$base
lá, o melhor que pude fazer foi a de três linhasname=${var##*/} ; base=${name%%.*} ; lastpart=${base#${base%???}}
. No lado positivo, é pura festa, mas ainda são 3 linhas. (No seu exemplo de "/tmp/file.txt", eu precisaria de "ile" em vez de "file".) Acabei de aprender muito sobre a substituição de parâmetros; Eu não tinha ideia de que poderia fazer isso ... muito útil. Eu também acho muito legível, pessoalmente.%
vez de%%
remover o sufixo, e na verdade não preciso desviar o caminho, para obter uma linha melhor, duasnoextn=${var%.*} ; lastpart=${noextn#${noextn%???}}
.$IFS
em${noextn}
e você não citar a expansão. Então, isso é mais seguro:lastpart=${noextn#"${noextn%???}"}
Se você pode usar
perl
:fonte
perl -e 'shift =~ /(.{3})\.[^.]*$/ && print $1' $filename
. Um adicionalbasename
seria necessário se o nome do arquivo não contenha sufixo, mas algum diretório no caminho contenha.perl -e 'shift =~ m#(.{3})(?:\.[^./]*)?$# && print $1' $filename
sed
trabalha para isso:Ou
Se o seu
sed
não suportar-r
, substitua as instâncias de()
com\(
e\)
, e então-r
não é necessário.fonte
Se o perl estiver disponível, acho que ele pode ser mais legível do que outras soluções, especificamente porque sua linguagem regex é mais expressiva e possui o
/x
modificador, que permite escrever regexs mais claros:Isso não imprime nada se não houver essa correspondência (se o nome da base não tiver extensão ou se a raiz antes da extensão for muito curta). Dependendo dos seus requisitos, você pode ajustar a regex. Este regex reforça as restrições:
Usar isso em uma substituição de comando tem os problemas normais com a remoção de muitas novas linhas finais, um problema que também afeta a resposta de Stéphane. Pode ser tratado nos dois casos, mas é um pouco mais fácil aqui:
fonte
Python2.7
fonte
Eu acho que essa função bash, pathStr (), fará o que você está procurando.
Não requer awk, sed, grep, perl ou expr. Ele usa apenas bash builtins, por isso é bastante rápido.
Também incluí as funções dependentes argsNumber e isOption, mas suas funcionalidades podem ser facilmente incorporadas ao pathStr.
A função dependente ifHelpShow não está incluída, pois possui inúmeras subdependências para a saída do texto de ajuda na linha de comando do terminal ou em uma caixa de diálogo da GUI via YAD . O texto de ajuda passado para ele é incluído na documentação. Informe se você gostaria do ifHelpShow e de seus dependentes.
RECURSOS
fonte
bash
ismos - aparentemente mais simples do que isso. Além disso, o que é${#@}
?$#
é a sintaxe do número de argumentos e também é usada em outros lugares aqui.${#@}
não é equivalente na maioria dos casos - a especificação POSIX indica os resultados de qualquer expansão de parâmetro em um$@
ou$*
não é especificado, infelizmente. Pode trabalhar embash
mas isso não é um recurso confiável, eu acho que é o que estou tentando dizer,.