题 在Bash中提取文件名和扩展名


我想分别获取文件名(没有扩展名)和扩展名。

我到目前为止找到的最佳解决方案是:

NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`

这是错误的,因为如果文件名包含多个,则它不起作用 . 字符。如果,让我们说,我有 a.b.js,它会考虑 a 和 b.js, 代替 a.b 和 js

它可以在Python中轻松完成

file, ext = os.path.splitext(path)

但是如果可能的话,我宁愿不为此启动Python解释器。

还有更好的想法?


1620
2018-06-08 14:00


起源


这个问题 解释了这个bash技术和其他几个相关的技术。 - jjclarkson
在应用下面的好答案时,不要像我在这里显示的那样简单地粘贴你的变量 错误:  extension="{$filename##*.}" 就像我做了一段时间!移动 $ 在卷曲之外: 对:  extension="${filename##*.}" - Chris K
这显然是一个非常重要的问题,对我来说,很难说下面的答案是否完全正确。令人惊讶的是,这不是(ba)sh中的内置操作(答案似乎使用模式匹配来实现该功能)。我决定使用Python os.path.splitext 如上所述...... - Peter Gibson
同意 - 同样的评论。是否没有可用的编译/可编译实用程序,它可以针对无数的用例进行命令行切换 - 例如。取而代之的是延伸,或者是n。分隔符,或检查二进制标题中的文件类型等。 。? - Alex Hall
如 延期 必须代表 性质 一个文件,有一个 魔法 命令哪个检查文件来判断他的性质和贡献 标准扩展。看到 我的答案 - F. Hauri


答案:


首先,获取没有路径的文件名:

filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"

或者,您可以专注于路径的最后一个'/'而不是'。'即使你有不可预测的文件扩展名,它应该工作:

filename="${fullfile##*/}"

2815
2018-06-08 14:05



查看 gnu.org/software/bash/manual/html_node/... 完整的功能集。 - D.Shawley
在“$ fullfile”中添加一些引号,否则您将冒险破坏文件名。 - lhunath
哎呀,你甚至可以写filename =“$ {fullfile ## * /}”并避免多打电话 basename - ephemient
如果文件没有扩展名,则此“解决方案”不起作用 - 而是输出整个文件名,考虑到没有扩展名的文件无处不在,这是非常糟糕的。 - nccc
修复了处理没有扩展名的文件名: extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo '')。请注意,如果是扩展名 是 现在,它将返回包括初始 .,例如, .txt。 - mklement0


~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz

有关详细信息,请参阅 shell参数扩展 在Bash手册中。


503
2018-06-08 14:05



如果文件名的“扩展”部分有两个点,你(也许是无意中)提出了一个很好的问题,如.tar.gz ......我从未考虑过这个问题,我怀疑它是如果不事先知道所有可能的有效文件扩展名,就无法解决。 - rmeador
为什么不能解决?在我的示例中,应该认为该文件包含 二 扩展,而不是带有两个点的扩展。您可以单独处理两个扩展。 - Juliano
它在词汇基础上无法解决,您需要检查文件类型。考虑你是否有一个叫做的游戏 dinosaurs.in.tar 然后你把它压缩了 dinosaurs.in.tar.gz :) - porges
如果您传递完整路径,这会变得更复杂。我的一个人有'。'在路径中间的目录中,但文件名中没有。示例“a / b.c / d / e / filename”将结束“.c / d / e / filename” - Walt Sellers
显然没有 x.tar.gz的延伸是 gz 和文件名是 x.tar 这就对了。没有双重扩展这样的东西。我很确定boost :: filesystem会这样处理它。 (split path,change_extension ...)如果我没有弄错的话,它的行为是基于python的。 - v.oddou


通常您已经知道扩展名,因此您可能希望使用:

basename filename .extension

例如:

basename /path/to/dir/filename.txt .txt

我们得到了

filename

281
2017-10-19 10:56



第二个论点 basename 非常令人大开眼界,ty先生/女士:) - akaIDIOT
以及如何使用这种技术提取扩展? ;) 等一下!我们实际上并不知道它。 - Tomasz Gandor
假设您有一个以任何结尾的压缩目录 .zip 要么 .ZIP。有没有办法可以做点什么 basename $file {.zip,.ZIP}? - Dennis
虽然这只回答了OP问题的一部分,但它确实回答了我输入谷歌的问题。 :-)非常光滑! - sudo make install


你可以使用POSIX变量的神奇之处:

bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo ${FILENAME%%.*}
somefile
bash-3.2$ echo ${FILENAME%.*}
somefile.tar

有一点需要注意,如果你的文件名是形式的 ./somefile.tar.gz 然后 echo ${FILENAME%%.*} 会贪婪地删除最长的匹配 . 而且你有空字符串。

(您可以使用临时变量解决此问题:

FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}


这个 现场 解释更多。

${variable%pattern}
  Trim the shortest match from the end
${variable##pattern}
  Trim the longest match from the beginning
${variable%%pattern}
  Trim the longest match from the end
${variable#pattern}
  Trim the shortest match from the beginning

125
2018-02-05 09:09



比约阿希姆的回答简单得多,但我总是要查找POSIX变量替换。此外,这在Max OSX上运行 cut 没有 --complement 和 sed 没有 -r。 - jwadsack


如果文件没有扩展名或没有文件名,那似乎不起作用。这是我正在使用的;它只使用内置函数并处理更多(但不是全部)病态文件名。

#!/bin/bash
for fullpath in "$@"
do
    filename="${fullpath##*/}"                      # Strip longest match of */ from start
    dir="${fullpath:0:${#fullpath} - ${#filename}}" # Substring from 0 thru pos of filename
    base="${filename%.[^.]*}"                       # Strip shortest match of . plus at least one non-dot char from end
    ext="${filename:${#base} + 1}"                  # Substring from len of base thru end
    if [[ -z "$base" && -n "$ext" ]]; then          # If we have an extension and no base, it's really the base
        base=".$ext"
        ext=""
    fi

    echo -e "$fullpath:\n\tdir  = \"$dir\"\n\tbase = \"$base\"\n\text  = \"$ext\""
done

以下是一些测试用例:

$ basename-and-extension.sh / / home / me / / home / me / file /home/me/file.tar /home/me/file.tar.gz /home/me/.hidden / home / me / .hidden.tar / home / me / ..
/:
    dir =“/”
    base =“”
    ext =“”
/家/我/:
    dir =“/ home / me /”
    base =“”
    ext =“”
/家/我/文件:
    dir =“/ home / me /”
    base =“file”
    ext =“”
/home/me/file.tar:
    dir =“/ home / me /”
    base =“file”
    ext =“tar”
/home/me/file.tar.gz:
    dir =“/ home / me /”
    base =“file.tar”
    ext =“gz”
/home/me/.hidden:
    dir =“/ home / me /”
    base =“。hidden”
    ext =“”
/home/me/.hidden.tar:
    dir =“/ home / me /”
    base =“。hidden”
    ext =“tar”
/家/我/ ..:
    dir =“/ home / me /”
    base =“..”
    ext =“”
。:
    dir =“”
    base =“。”
    ext =“”

65
2017-09-10 05:17



代替 dir="${fullpath:0:${#fullpath} - ${#filename}}" 我经常看到 dir="${fullpath%$filename}"。写起来比较简单。不确定是否存在任何真正的速度差异或陷阱。 - dubiousjim
这使用#!/ bin / bash,这几乎总是错误的。如果可能的话,首选#!/ bin / sh或者如果没有,请选择#!/ usr / bin / env bash。 - Good Person
@Good Person:我不知道它几乎总是错的: which bash  - > /bin/bash ;也许这是你的发行版? - vol7ron
@ vol7ron - 很多发行版bash都在/ usr / local / bin / bash中。在OSX上,许多人在/ opt / local / bin / bash中安装了更新的bash。因此/ bin / bash是错误的,应该使用env来查找它。更好的是使用/ bin / sh和POSIX结构。除了solaris,这是一个POSIX shell。 - Good Person
@GoodPerson但是如果你对bash更熟悉,为什么要用sh?这不就是说,为什么在使用sh时使用Perl? - vol7ron


您可以使用 basename

例:

$ basename foo-bar.tar.gz .tar.gz
foo-bar

您需要提供带有要删除的扩展名的basename,但是如果您一直在执行 tar 同 -z 然后你就知道扩展名了 .tar.gz

这应该做你想要的:

tar -zxvf $1
cd $(basename $1 .tar.gz)

40
2018-02-05 08:50



我想 cd $(basename $1 .tar.gz) 适用于.gz文件。但他提到了问题 Archive files have several extensions: tar.gz, tat.xz, tar.bz2 - SS Hegde
2年前,Tomi Po发布了相同的内容。 - Blauhirn
嗨Blauhirn,wauw这是一个古老的问题。我觉得日期发生了一些事情。我特别记得在问到这个问题之后不久就回答了这个问题,那里只有其他几个答案。可能是问题与另一个问题合并了,那么这样做吗? - Bjarke Freund-Hansen
是的,我没记错。我原来回答这个问题 stackoverflow.com/questions/14703318/... 在被问到的同一天,2年后它被合并到这一个。当我的回答以这种方式移动时,我很难被指责一个重复的答案。 - Bjarke Freund-Hansen


梅伦在博客文章评论中写道:

使用Bash,也有 ${file%.*} 获取没有扩展名的文件名 ${file##*.} 单独获得扩展。那是,

file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"

输出:

filename: thisfile
extension: txt

24
2017-07-21 10:24



这种语法究竟是如何工作的? - syntagma
@REACHUS:见 gnu.org/software/bash/manual/html_node/... - mklement0