标准unix util将“foo.bar.baz”拆分为“foo.bar”+“.baz”?

标准unix util将“foo.bar.baz”拆分为“foo.bar”+“.baz”?

我想拆分一个 string $basename,其中包含两部分的输出$stem$ext,这样:

  1. 该字符串"${stem}${ext}"与原始字符串相同$basename
  2. $stem可以$ext为空(取决于 中的字符串$basename);
  3. 如果$ext不为空,则必须以 开头,并且此后.不再包含。.

编写一个 shell 函数来做到这一点并不困难,但在这样做之前我想知道是否有一个标准的 Unix 命令可以做到这一点。

编辑:

FWIW,我在 ( ) 脚本中编码的方式zsh

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

编辑:修复了拼写错误(${base#...-> ${basename#...),并纳入了 Stephane Chazelas 的建议(...#$stem-> ...#"$stem")。

答案1

您可以仅使用 Bash 中包含的普通字符串解析函数来执行非常接近您想要的操作:

$ F="foo.bar.baz"

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

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

所以你可以像这样把它放在一起:

$ $ F="foo.bar.baz"

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

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

摘自 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.

您可以在以下位置阅读有关这些功能的信息Bash 手册页

答案2

不能有命令设置 shell 的变量,因为变量是 shell 进程内部的东西,所以另一个命令,生活在它自己的进程中,不可能改变它。

使用 shell,您可以执行以下操作:

var=$(some-command)

检索命令的输出(不带尾随换行符),但如果您需要命令的两个结果,那就会变得更加棘手。

一种方法如下:

eval "$(some-command)"

其中 some-command 输出如下内容:

var1='something' var2='someotherthing'

但在你问之前,没有这样的标准命令可以采用路径并将其拆分为目录、基本名称和扩展名(无论这意味着什么)。

现在,外壳本身可能具有用于此目的的内部功能。例如cshzsh有可以给你头部、尾部、延伸的修饰符。像zsh

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

.现在您可能需要考虑对于像or ../.bashrcor foo.tar.gz?这样的文件来说,这些应该是什么?

现在,如果您正在寻找标准 POSIX 语法,那么您已经(几乎)拥有它了:rest=${file%.*}${file#$rest}zsh具体的。在 POSIX 语法中,您需要ext=${file#"$rest"}, 否则$rest被视为模式。请注意,如果$file包含带有字符的路径/(例如foo.d/bar),可能不会执行您想要的操作。

答案3

如果字符串不包含换行符:

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

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

答案4

假设“foo.bar”。如果 ext 丢失并且总是有两个或三个句点,则为有效输出,这应该适用于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

相关内容