Utilitário unix padrão para dividir “foo.bar.baz” em “foo.bar” + “.baz”?

4

Eu quero dividir uma string $basename , contendo a saída em duas partes, $stem e $ext , de forma que:

  1. a string "${stem}${ext}" é idêntica à string original $basename ;
  2. $stem ou $ext pode estar vazio (dependendo da sequência em $basename );
  3. se $ext não estiver vazio, deve começar com . e não conter mais . depois disso.

Não é difícil escrever uma função de shell para fazer isso, mas antes disso eu gostaria de saber se existe um comando padrão do Unix que faz isso.

EDITAR:

FWIW, a maneira que eu codificaria isso em um script ( zsh ) seria

stem=${basename%.*}
ext=${basename#"$stem"}

EDIT: erro de digitação fixo ( ${base#... - > ${basename#... ), e incorporou a sugestão de Stephane Chazelas ( ...#$stem - > ...#"$stem" ).

    
por kjo 27.06.2013 / 19:07

5 respostas

2

Não pode haver comandos que definam variáveis do seu shell, porque as variáveis são algo interno ao processo shell, então outro comando, vivendo em seu próprio processo, não poderia alterar isso.

Com o seu shell, você pode fazer coisas como:

var=$(some-command)

para recuperar a saída de um comando (sem os caracteres de nova linha à direita), mas se você precisar de dois resultados de um comando, é quando se torna mais complicado.

Um método é como:

eval "$(some-command)"

Onde algumas saídas de comando, como:

var1='something' var2='someotherthing'

Mas antes que você pergunte, não existe um comando padrão que use um caminho e o divida no diretório, no nome de base e na extensão (o que quer que isso signifique) dessa maneira.

Agora, os próprios shells podem ter recursos internos para isso. Por exemplo, csh e zsh têm modificadores que podem dar a você cabeça, cauda, extensão. Como em zsh :

file=/path/to/foo.bar/
head=$file:h
tail=$file:t
ext=$file:e
rest=$file:r

Agora, convém considerar quais devem ser aquelas para um arquivo como . ou .. , / , .bashrc ou foo.tar.gz ?

Agora, se você está procurando pela sintaxe POSIX padrão, você já a tem (quase): rest=${file%.*} . ${file#$rest} é zsh específico. Na sintaxe POSIX, você precisa de ext=${file#"$rest"} , caso contrário, $rest é considerado um padrão. Cuidado, pois pode não fazer o que você deseja se $file contiver um caminho com / caracteres (como foo.d/bar ).

    
por 27.06.2013 / 21:56
3

Você pode usar apenas as funções de análise de string simples que estão incluídas no Bash para fazer algo muito próximo do que você deseja:

$ F="foo.bar.baz"

$ echo ${F%.*}
foo.bar

$ echo ${F/*./.}
.baz

Então você pode colocar isso da seguinte maneira:

$ $ F="foo.bar.baz"

$ stem=${F%.*}
$ ext=${F/*./.}

echo "${stem}${ext}"
foo.bar.baz

trechos da página man do Bash

$ {F%. *}
${parameter%word}
${parameter%%word}
   Remove  matching suffix pattern.  The word is expanded to produce a pattern 
   just as in pathname expansion.  If the pattern matches a trailing portion
   of the expanded value of parameter, then the result of  the  expansion  is
   the expanded  value  of  parameter with the shortest matching pattern (the
   ''%'' case) or the longest matching pattern (the ''%%'' case) deleted.  If
   parameter is @ or *, the pattern removal operation is applied  to  each
   positional parameter  in turn, and the expansion is the resultant list.  
   If parameter is an array variable subscripted with @ or *, the pattern
   removal operation is applied to each member of the array in  turn,  and  
   the  expansion  is  the resultant list.
$ {/*./.}
${parameter/pattern/string}
   Pattern  substitution.   The pattern is expanded to produce a pattern just
   as in pathname expansion.  Parameter is expanded and the longest match of 
   pattern against its value is replaced with string.  If pattern  begins 
   with  /, all  matches  of  pattern are replaced with string.  Normally
   only the first match is replaced.  If pattern begins with #, it must match
   at the beginning of the expanded value of parameter.  If pattern  begins
   with  %,  it  must match  at the end of the expanded value of parameter.
   If string is null, matches of pattern are deleted and the / following
   pattern may be omitted.  If parameter is @ or *, the substitution operation
   is  applied  to  each  positional  parameter in turn, and the expansion 
   is the resultant list.  If parameter is an array variable subscripted
   with @ or *, the substitution operation is applied to each member of the
   array in turn, and the expansion  is  the resultant list.

Você pode ler sobre esses recursos na página de manual do Bash .

    
por 27.06.2013 / 20:41
0

Se as strings não contiverem novas linhas:

start cmd:> echo foo.bar.baz | sed 's/\(^.*\)\(\.[^\.]*$\)//'
foo.bar

start cmd:> echo foo.bar.baz | sed 's/\(^.*\)\(\.[^\.]*$\)//'
.baz
    
por 27.06.2013 / 19:18
0

Eu não vi nenhum comando Unix padrão para isso. Eu usei isso dentro de scripts de shell:

fullname=foo.bar.baz
basename="$(python -c "import os; print os.path.splitext('"$fullname"')[0]")"
extension="$(python -c "import os; print os.path.splitext('"$fullname"')[1]")"

como eu acho mais fácil de acertar do que expressões regulares. YMMV.

    
por 27.06.2013 / 20:01
0

Assumindo "foo.bar". é uma saída válida se ext estiver ausente e que sempre haverá dois ou três períodos, isso deve funcionar com awk :

basename='foo.bar.baz'
stem=$(echo $basename | awk '{ split($1,a,"."); print a[1] "." a[2]; }')
ext=$(echo $basename | awk '{ split($1,a,"."); print "." a[3]; }');
echo $stem$ext
    
por 27.06.2013 / 21:37