Utilitaire standard d'unix pour split "foo.bar.baz" en "foo.bar" + ".baz"?

Je veux split une string $basename , contenant la sortie en deux parties, $stem et $ext , de sorte que:

  1. la string "${stem}${ext}" est identique à la string d'origine $basename ;
  2. $stem ou $ext peuvent être vides (en fonction de la string dans $basename );
  3. si $ext n'est pas vide, il doit commencer par . , et ne contiennent plus . par la suite.

Il n'est pas difficile d'écrire une fonction shell pour le faire, mais avant cela, j'aimerais savoir s'il existe une command standard d'Unix.

MODIFIER:

FWIW, la façon dont je coderais ceci dans un script ( zsh ) serait

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

EDIT: correction de typo ( ${base#... -> ${basename#... ), et a incorporé la suggestion de Stephane Chazelas ( ...#$stem -> ...#"$stem" ).

Il ne peut pas y avoir de commands qui définissent les variables de votre shell, parce que les variables sont quelque chose d'interne au process shell, donc une autre command, vivant dans son propre process, ne pourrait pas altérer cela.

Avec votre shell, vous pouvez faire des choses comme:

 var=$(some-command) 

pour récupérer la sortie d'une command (sans les caractères de return arrière), mais si vous avez besoin de deux résultats d'une command, c'est quand cela devient plus compliqué.

Une méthode est comme:

 eval "$(some-command)" 

Lorsque certaines commands produisent des choses comme:

 var1='something' var2='someotherthing' 

Mais avant de requestr, il n'y a pas de telle command standard qui prend un path et le divise dans le directory, le nom de base et l'extension (peu importe ce que cela signifie) de telle manière.

Maintenant, les coquilles elles-mêmes peuvent avoir des caractéristiques internes pour cela. Par exemple, csh et zsh ont des modificateurs qui peuvent vous donner la tête, la queue, l'extension. Comme dans zsh :

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

Maintenant, vous voudrez peut-être considérer ce que ces devraient être pour un file comme . ou .. , / , .bashrc ou foo.tar.gz ?

Maintenant, si vous cherchez la syntaxe POSIX standard, vous l'avez déjà (presque): rest=${file%.*} . ${file#$rest} est spécifique à zsh . Dans la syntaxe POSIX, vous avez besoin de ext=${file#"$rest"} , sinon $rest est pris comme model. Attention, cela peut ne pas faire ce que vous voulez si $file contient un path avec / characters (comme foo.d/bar ).

Vous pouvez utiliser simplement des fonctions d'parsing de string simples incluses dans Bash pour faire quelque chose de très proche de ce que vous voulez:

 $ F="foo.bar.baz" $ echo ${F%.*} foo.bar $ echo ${F/*./.} .baz 

Donc, vous pourriez le mettre set comme ça:

 $ $ F="foo.bar.baz" $ stem=${F%.*} $ ext=${F/*./.} echo "${stem}${ext}" foo.bar.baz 

extraits de la page de manuel de 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/ssortingng} 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 ssortingng. If pattern begins with /, all matches of pattern are replaced with ssortingng. 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 ssortingng 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. 

Vous pouvez lire à propos de ces fonctionnalités dans la page de manuel Bash .

Si les strings ne contiennent pas de nouvelles lignes:

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

Je n'ai pas vu de commands Unix standard pour cela. Je l'ai utilisé dans les scripts 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]")" 

car je trouve cela plus facile à get que les expressions régulières. YMMV.

En supposant que "foo.bar." est une sortie valide si l'extension est manquante et qu'il y aura toujours deux ou trois périodes, cela devrait fonctionner avec 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