我想拆分一个 string $basename
,其中包含两部分的输出$stem
和$ext
,这样:
- 该字符串
"${stem}${ext}"
与原始字符串相同$basename
; - 或
$stem
可以$ext
为空(取决于 中的字符串$basename
); - 如果
$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'
但在你问之前,没有这样的标准命令可以采用路径并将其拆分为目录、基本名称和扩展名(无论这意味着什么)。
现在,外壳本身可能具有用于此目的的内部功能。例如csh
,zsh
有可以给你头部、尾部、延伸的修饰符。像zsh
:
file=/path/to/foo.bar/
head=$file:h
tail=$file:t
ext=$file:e
rest=$file:r
.
现在您可能需要考虑对于像or ..
、/
、.bashrc
or 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