14

对不起,蹩脚的 bash 问题,但我似乎无法解决这个问题。

我有以下简单案例:

  • 我有变量artifact-1.2.3.zip

  • 我想在连字符和点的最后一个索引之间获得一个子字符串(都是独占的)。

我的bash技能不是太强。我有以下内容:

a="artifact-1.2.3.zip"; b="-"; echo ${a:$(( $(expr index "$a" "$b" + 1) - $(expr length "$b") ))}

生产:

1.2.3.zip

我如何也删除该.zip部分?

4

4 回答 4

32

标题为“变量替换”的bash手册页部分描述了如何使用、、和。${var#pattern}${var##pattern}${var%pattern}${var%%pattern}

假设您有一个名为 的变量filename,例如,

filename="artifact-1.2.3.zip"

然后,以下是基于模式的提取:

% echo "${filename%-*}"
artifact

% echo "${filename##*-}"
1.2.3.zip

为什么我使用##而不是#

如果文件名可能包含破折号,例如:

filename="multiple-part-name-1.2.3.zip"

然后比较以下两个替换:

% echo "${filename#*-}"
part-name-1.2.3.zip

% echo "${filename##*-}"
1.2.3.zip

提取版本和扩展后,要隔离版本,请使用:

% verext="${filename##*-}"
% ver="${verext%.*}"
% ext="${verext##*.}"
% echo $ver
1.2.3
% echo $ext
zip
于 2013-04-22T18:21:31.343 回答
16
$ a="artifact-1.2.3.zip"; a="${a#*-}"; echo "${a%.*}"

‘<code>#pattern’ removes pattern so long as it matches the beginning of $a. The syntax of pattern is similar to that used in filename matching. In our case,

  • * is any sequence of characters.
  • - means a literal dash.
  • Thus #*- matches everything up to, and including, the first dash.
  • Thus ${a#*-} expands to whatever $a would expand to, except that artifact- is removed from the expansion, leaving us with 1.2.3.zip.

Similarly, ‘<code>%pattern’ removes pattern so long as it matches the end of the expansion. In our case,

  • . a literal dot.
  • * any sequence of characters.
  • Thus %.* is everything including the last dot up to the end of the string.
  • Thus if $a expands to 1.2.3.zip, then ${a%.*} expands to 1.2.3.

Job done.

The man page content for this is as follows (at least on my machine, YMMV):

       ${parameter#word}
       ${parameter##word}
              The word is expanded to produce a pattern just  as  in  pathname
              expansion.  If the pattern matches the beginning of the 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%word}
       ${parameter%%word}
              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 pat-
              tern (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.

HTH!

EDIT

Kudos to @x4d for the detailed answer. Still think people should RTFM though. If they don't understand the manual, then post another question.

于 2013-04-22T17:51:54.957 回答
3

使用 Bash RegEx 功能:

>str="artifact-1.2.3.zip"
[[ "$str" =~ -(.*)\.[^.]*$ ]] && echo ${BASH_REMATCH[1]}
于 2013-04-22T17:58:24.903 回答
0

I think you can do this:

string=${a="artifact-1.2.3.zip"; b="-"; echo ${a:$(( $(expr index "$a" "$b" + 1) - $(expr length "$b") ))}}

substring=${string:0:4}

The last step removes the last 4 characters from the string. There's some more info on here.

于 2013-04-22T17:53:31.593 回答