467

给定一个字符串文件路径,例如/foo/fizzbuzz.bar,我将如何使用 bash 仅提取fizzbuzz所述字符串的一部分?

4

14 回答 14

671

以下是如何使用 Bash 中的 # 和 % 运算符来完成此操作。

$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz

${x%.bar}也可以${x%.*}是删除一个点之后的所有内容或${x%%.*}删除第一个点之后的所有内容。

例子:

$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz

文档可以在Bash 手册中找到。寻找${parameter%word}${parameter%%word}尾随部分匹配部分。

于 2008-09-24T03:54:28.167 回答
274

查看 basename 命令:

NAME="$(basename /foo/fizzbuzz.bar .bar)"

指示它删除后缀.bar,导致NAME=fizzbuzz

于 2008-09-24T03:38:03.663 回答
57

纯 bash,在两个单独的操作中完成:

  1. 从路径字符串中删除路径:

    path=/foo/bar/bim/baz/file.gif
    
    file=${path##*/}  
    #$file is now 'file.gif'
    
  2. 从路径字符串中删除扩展名:

    base=${file%.*}
    #${base} is now 'file'.
    
于 2014-05-06T14:20:03.097 回答
21

使用 basename 我使用以下内容来实现此目的:

for file in *; do
    ext=${file##*.}
    fname=`basename $file $ext`

    # Do things with $fname
done;

这不需要文件扩展名的先验知识,即使您的文件名在其文件名中包含点(在其扩展名前面)也可以工作;虽然它确实需要该程序basename,但这是 GNU coreutils 的一部分,因此它应该随任何发行版一起提供。

于 2014-04-11T17:07:04.220 回答
17

basename 和 dirname 函数是您所追求的:

mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")

有输出:

basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
于 2008-09-24T03:40:06.123 回答
14

纯 bash 方式:

~$ x="/foo/bar/fizzbuzz.bar.quux.zoom"; 
~$ y=${x/\/*\//}; 
~$ echo ${y/.*/}; 
fizzbuzz

此功能在“参数扩展”下的 man bash 中进行了解释。非 bash 方式比比皆是:awk、perl、sed 等等。

编辑:适用于文件后缀中的点并且不需要知道后缀(扩展名),但不适用于名称本身中的点。

于 2008-09-24T03:39:05.130 回答
7

使用basename假设您知道文件扩展名是什么,不是吗?

而且我相信各种正则表达式建议无法处理包含多个“。”的文件名。

以下似乎应对双点。哦,还有包含“/”的文件名(只是为了好玩)

套用 Pascal 的话说,“抱歉这个脚本太长了。我没有时间把它缩短”


  #!/usr/bin/perl
  $fullname = $ARGV[0];
  ($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
  ($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
  print $basename . "\n";
 
于 2008-09-24T05:07:37.880 回答
5

除了此答案中使用的符合 POSIX 的语法外,

basename string [suffix]

如在

basename /foo/fizzbuzz.bar .bar

GNUbasename支持另一种语法:

basename -s .bar /foo/fizzbuzz.bar

结果相同。-s区别和优势在于-a,它支持多个参数:

$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar

这甚至可以通过使用该选项将输出与 NUL 字节分隔开来使文件名安全-z,例如对于这些包含空格、换行符和全局字符的文件(由 引用ls):

$ ls has*
'has'$'\n''newline.bar'  'has space.bar'  'has*.bar'

读入一个数组:

$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")

readarray -d需要 Bash 4.4 或更高版本。对于旧版本,我们必须循环:

while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
于 2018-10-24T22:01:38.080 回答
3
perl -pe 's/\..*$//;s{^.*/}{}'
于 2008-09-24T03:39:34.317 回答
3

如果您不能按照其他帖子中的建议使用 basename,则始终可以使用 sed。这是一个(丑陋的)例子。它不是最好的,但它通过提取想要的字符串并用想要的字符串替换输入来工作。

echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'

这将为您提供输出

嘶嘶声

于 2008-09-24T11:12:06.803 回答
2

注意建议的 perl 解决方案:它会删除第一个点之后的任何内容。

$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some

如果你想用 perl 来做,这可以:

$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with

但是,如果您使用的是 Bash,则使用y=${x%.*}(或者basename "$x" .ext如果您知道扩展名)的解决方案要简单得多。

于 2010-06-19T10:35:04.550 回答
1

基本名称会这样做,删除路径。如果给定后缀,并且它与文件的后缀匹配,它还将删除后缀,但您需要知道赋予命令的后缀。否则,您可以使用 mv 并以其他方式找出新名称应该是什么。

于 2008-11-15T00:47:41.193 回答
1

将评分最高的答案与第二名的答案相结合,以获得没有完整路径的文件名:

$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
于 2014-08-20T18:30:44.667 回答
-2

您可以使用

mv *<PATTERN>.jar "$(basename *<PATTERN>.jar <PATTERN>.jar).jar"

例如:- 我想-SNAPSHOT从我的文件名中删除。对于下面使用的命令

 mv *-SNAPSHOT.jar "$(basename *-SNAPSHOT.jar -SNAPSHOT.jar).jar"
于 2021-07-19T08:35:02.493 回答