关于Linux:在bash中提取不带路径和扩展名的文件basename

Extract file basename without path and extension in bash

本问题已经有最佳答案,请猛点这里访问。

给定的文件名如下:

1
2
/the/path/foo.txt
bar.txt

我希望得到:

1
2
foo
bar

为什么这样不行?

1
2
3
4
5
6
#!/bin/bash

fullfile=$1
fname=$(basename $fullfile)
fbname=${fname%.*}
echo $fbname

正确的方法是什么?


您不必调用外部basename命令。相反,您可以使用以下命令:

1
2
3
4
5
6
7
8
$ s=/the/path/foo.txt
$ echo ${s##*/}
foo.txt
$ s=${s##*/}
$ echo ${s%.txt}
foo
$ echo ${s%.*}
foo

请注意,此解决方案应适用于最近(2004年后)所有符合POSIX的shell(如bashdashksh等)。

来源:shell命令语言2.6.2参数扩展

关于bash字符串操作的更多信息:http://tldp.org/ldp/lg/issue18/bash.html


basename命令有两种不同的调用;一种是只指定路径,在这种情况下,它会给出最后一个组件,而在另一种情况下,它还会给出一个将要删除的后缀。因此,您可以使用第二次调用basename来简化示例代码。此外,请注意正确引用以下内容:

1
2
fbname=$(basename"$1" .txt)
echo"$fbname"


basename和cut的组合效果很好,即使是像.tar.gz这样的双端:

1
fbname=$(basename"$fullfile" | cut -d. -f1)

如果这个解决方案比bash参数扩展需要更少的算术能力,那就很有趣了。


bash,无basename,无变戏法。设置字符串和echo

1
2
s=/the/path/foo.txt
echo ${s//+(*\/|.*)}

输出:

1
foo

注:bashextglob选项必须为"on"(在Ubuntu上默认为"on"),否则,请执行以下操作:

1
shopt -s extglob

走过${s//+(*\/|.*)}

  • ${s--从$s开始。
  • //替代了模式的每个实例。
  • +(匹配括号中的一个或多个模式列表。
  • *\/匹配/之前的任何内容。(第一种模式)
  • |或。(模式分隔符。)
  • .*匹配.之后的任何内容。(第二种模式)
  • )端模式列表。
  • }结束参数扩展——由于没有/(在字符串替换之前),匹配的模式将被删除。
  • 相关man bash背景:

  • 模式替换:
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      ${parameter/pattern/string}
              Pattern substitution.  The pattern is expanded to produce a pat‐
              tern 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 begin‐
              ning 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 / fol‐
              lowing pattern may be omitted.  If parameter is @ or *, the sub‐
              stitution  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.
  • 扩展模式匹配:
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      If the extglob shell option is enabled using the shopt builtin, several
       extended  pattern  matching operators are recognized.  In the following
       description, a pattern-list is a list of one or more patterns separated
       by a |.  Composite patterns may be formed using one or more of the fol‐
       lowing sub-patterns:

              ?(pattern-list)
                     Matches zero or one occurrence of the given patterns
              *(pattern-list)
                     Matches zero or more occurrences of the given patterns
              +(pattern-list)
                     Matches one or more occurrences of the given patterns
              @(pattern-list)
                     Matches one of the given patterns
              !(pattern-list)
                     Matches anything except one of the given patterns

    以下是一句话:

  • $(basename ${s%.*})
  • $(basename ${s} .${s##*.})
  • 我需要这个,就像邦邦和W4etwetwet要求的那样。


    以下是获取文件名或扩展名的另一种(更复杂)方法,首先使用rev命令反转文件路径,从第一个.中剪切,然后再次反转文件路径,如下所示:

    1
    2
    filename=`rev <<<"$1" | cut -d"." -f2- | rev`
    fileext=`rev <<<"$1" | cut -d"." -f1 | rev`


    如果您想使用Windows文件路径(在cygwin下)进行良好的播放,也可以尝试以下操作:

    1
    fname=${fullfile##*[/|\\]}

    在Windows上使用bash时,这将解释反斜杠分隔符。


    只是我想出的一个替代方法来提取一个扩展,使用这个线程中的文章和我自己更熟悉的小知识库。

    1
    ext="$(rev <<<"$(cut -f"1" -d"." <<<"$(rev <<<"file.docx")")")"

    注意:请建议我使用报价单;它对我有效,但我可能会错过一些关于它们正确使用的内容(我可能使用太多)。


    使用basename命令。它的主页在这里:http://unixhelp.ed.ac.uk/cgi/man-cgi?基名