代码之家  ›  专栏  ›  技术社区  ›  Redwood

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

  •  336
  • Redwood  · 技术社区  · 16 年前

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

    13 回复  |  直到 6 年前
        1
  •  469
  •   Zan Lynx    8 年前

    下面介绍如何使用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 manual . 寻找 ${parameter%word} ${parameter%%word} 尾随部分匹配部分。

        2
  •  183
  •   zigdon    16 年前

    查看basename命令:

    NAME=`basename /foo/fizzbuzz.bar .bar`
    
        3
  •  33
  •   Arseni Mourzenko    10 年前

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

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

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

      base=${file%.*}
      #${base} is now 'file'.
      
        4
  •  15
  •   mike    11 年前

    我使用basename实现了以下目标:

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

    这不需要预先知道文件扩展名,即使文件名中有点(在扩展名前面),也能正常工作;它确实需要程序 basename 不过,这是GNU coreutils的一部分,所以它应该与任何发行版一起发行。

        5
  •  12
  •   Nick Lockwood    12 年前

    纯巴什方式:

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

    这个功能在man bash上的“参数扩展”下进行了解释。非bash方式有很多:awk、perl、sed等等。

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

        6
  •  7
  •   Jerub    6 年前

    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
    
        7
  •  3
  •   mopoke    16 年前
    perl -pe 's/\..*$//;s{^.*/}{}'
    
        8
  •  3
  •   Andrew Edgecombe    16 年前

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

    我相信,各种正则表达式建议不适用于包含多个“”的文件名。

    下面似乎是处理双点。哦,还有文件名包含一个“/”本身(只是为了好玩)

    用帕斯卡的话说,“对不起,这个剧本太长了。我没时间把它缩短了”

    
      #!/usr/bin/perl
      $fullname = $ARGV[0];
      ($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
      ($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
      print $basename . "\n";
     
        9
  •  3
  •   Benjamin W.    6 年前

    除了 POSIX conformant syntax 用于 this answer ,

    basename string [suffix]

    如在

    basename /foo/fizzbuzz.bar .bar
    

    GNU basename 支持另一种语法:

    basename -s .bar /foo/fizzbuzz.bar
    

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

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

    甚至可以通过使用 -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*)
    
        10
  •  2
  •   nymacro    16 年前

    如果你不能像其他帖子中建议的那样使用basename,你可以一直使用sed。这里有一个(丑陋的)例子。它不是最大的,但它通过提取所需的字符串并用所需的字符串替换输入来工作。

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

    这将为您提供输出

    费兹巴兹

        11
  •  2
  •   mivk    15 年前

    注意建议的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 如果你知道扩展名的话)就简单多了。

        12
  •  1
  •   c.gutierrez    10 年前

    将最高评级的答案与第二个最高评级的答案组合以获取不带完整路径的文件名:

    $ x="/foo/fizzbuzz.bar.quux"
    $ y=(`basename ${x%%.*}`)
    $ echo $y
    fizzbuzz
    
        13
  •  0
  •   waynecolvin    16 年前

    basename会这样做,删除路径。如果给定后缀,并且它与文件的后缀匹配,那么它也将删除后缀,但是您需要知道给命令指定的后缀。否则,您可以使用mv并找出新名称应该是另一种方式。