如何从Bash中的路径字符串中删除文件后缀和路径部分?

Law*_*ton 366 string bash filenames

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

Zan*_*ynx 524

以下是使用Bash中的#和%运算符的方法.

$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
Run Code Online (Sandbox Code Playgroud)

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

例:

$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz
Run Code Online (Sandbox Code Playgroud)

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

  • @Basil:参数扩展.在控制台上键入"man bash",然后键入"/ parameter expansion" (14认同)
  • 什么叫$ {x%.bar}?我想了解更多相关信息. (7认同)

zig*_*don 205

看一下basename命令:

NAME=$(basename /foo/fizzbuzz.bar .bar)
Run Code Online (Sandbox Code Playgroud)

  • 可能是所有当前提供的解决方案中最简单的...虽然我使用$(...)而不是反引号. (11认同)
  • 最简单,但添加一个依赖(不是一个巨大或奇怪的,我承认).它还需要知道后缀. (6认同)
  • @xizdaqrian 这绝对是错误的。这是一个简单的程序,不需要半秒就可以返回。我刚刚执行时间 find /home/me/dev -name "*.py" .py -exec basename {} \; 它在 1 秒内总共删除了 1500 个文件的扩展名和目录。 (3认同)
  • 问题是时间紧迫。在观看bash使用基本名处理将近5分钟来处理800个文件之后,我刚刚在此讨论中搜索了问题。使用上述正则表达式方法,时间减少到大约7秒。尽管这个答案对于程序员来说更容易执行,但是所花费的时间却太多了。想象一下其中有数千个文件的文件夹!我有一些这样的文件夹。 (2认同)
  • 不过,尽可能避免外部流程的总体想法是合理的。以及 shell 编程的基本原则。 (2认同)

小智 36

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

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

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

    base=${file%.*}
    #${base} is now 'file'.
    
    Run Code Online (Sandbox Code Playgroud)


小智 17

使用basename我使用以下代码来实现此目的:

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

    # Do things with $fname
done;
Run Code Online (Sandbox Code Playgroud)

这不需要文件扩展名的先验知识,即使你的文件名在其文件名中有点(在它的扩展名前面),也可以工作; 它确实需要程序basename,但这是GNU coreutils的一部分,所以它应该随附任何发行版.

  • @metrix只需添加"." 在$ ext之前,即:`fname = \`basename $ file.$ ext \`` (3认同)

Vin*_*vic 12

纯粹的bash方式:

~$ x="/foo/bar/fizzbuzz.bar.quux.zoom"; 
~$ y=${x/\/*\//}; 
~$ echo ${y/.*/}; 
fizzbuzz
Run Code Online (Sandbox Code Playgroud)

在"参数扩展"下的man bash中解释了此功能.非bash方式比比皆是:awk,perl,sed等.

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


Jer*_*rub 10

basename和dirname函数就是你所追求的:

mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
Run Code Online (Sandbox Code Playgroud)

有输出:

basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
Run Code Online (Sandbox Code Playgroud)

  • 修复这里的引用会很有帮助——也许通过 http://shellcheck.net/ 使用 `mystring=$1` 而不是当前的常量值来运行它(这将抑制几个警告,确保不包含空格/glob字符/等),并解决它发现的问题? (2认同)

And*_*mbe 6

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

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

以下似乎处理双点。哦,还有包含“/”的文件名本身(只是为了踢球)

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


  #!/usr/bin/perl
  $fullname = $ARGV[0];
  ($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
  ($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
  print $basename . "\n";
 


Ben*_* W. 5

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

basename string [suffix]
Run Code Online (Sandbox Code Playgroud)

basename /foo/fizzbuzz.bar .bar
Run Code Online (Sandbox Code Playgroud)

GNUbasename支持另一种语法:

basename -s .bar /foo/fizzbuzz.bar
Run Code Online (Sandbox Code Playgroud)

结果相同。不同之处和优点是-s隐含-a,它支持多个参数:

$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Run Code Online (Sandbox Code Playgroud)

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

$ ls has*
'has'$'\n''newline.bar'  'has space.bar'  'has*.bar'
Run Code Online (Sandbox Code Playgroud)

读入数组:

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

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

while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
Run Code Online (Sandbox Code Playgroud)