我想分别获取文件名(不带扩展名)和扩展名。

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

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解释器。

有更好的主意吗?


当前回答

这是AWK的代码。这可以做得更简单。但我不擅长AWK。

filename$ ls
abc.a.txt  a.b.c.txt  pp-kk.txt
filename$ find . -type f | awk -F/ '{print $2}' | rev | awk -F"." '{$1="";print}' | rev | awk 'gsub(" ",".") ,sub(".$", "")'
abc.a
a.b.c
pp-kk
filename$ find . -type f | awk -F/ '{print $2}' | awk -F"." '{print $NF}'
txt
txt
txt

其他回答

一个简单的答案:

要扩展POSIX变量答案,请注意您可以执行更多有趣的模式。因此,对于此处详述的案例,您可以简单地执行以下操作:

tar -zxvf $1
cd ${1%.tar.*}

这将切断.tar.<something>的最后一次出现。

更一般地,如果要删除最后一次出现的<一些><其他>然后

${1.*.*}

应该工作正常。

上述答案的链接似乎已失效。这是一个很好的解释,可以直接在Bash中使用TLDP进行一系列字符串操作。

从上面的答案来看,模仿Python的最短一行代码

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

假设您的文件确实有扩展名

EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)

首先,获取不带路径的文件名:

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

或者,您可以关注路径的最后一个“/”,而不是“.”即使您有不可预知的文件扩展名,也可以使用:

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

您可能需要检查文档:

在网页“3.5.3外壳参数扩展”部分在bash手册页的“参数扩展”部分

魔术文件识别

除了关于堆栈溢出问题的许多好答案之外,我还想补充一点:

在Linux和其他unixen下,有一个名为file的神奇命令,它通过分析文件的一些第一个字节来进行文件类型检测。这是一个非常旧的工具,最初用于打印服务器(如果不是为…创建的,我不确定)。

file myfile.txt
myfile.txt: UTF-8 Unicode text

file -b --mime-type myfile.txt
text/plain

标准扩展可以在/etc/mime.types中找到(在我的Debian GNU/Linux桌面上。请参阅man file和man mime.types.也许您必须安装文件实用程序和mime支持包):

grep $( file -b --mime-type myfile.txt ) </etc/mime.types
text/plain      asc txt text pot brf srt

您可以创建一个bash函数来确定正确的扩展。有一个小样本(不完美):

file2ext() {
    local _mimetype=$(file -Lb --mime-type "$1") _line _basemimetype
    case ${_mimetype##*[/.-]} in
        gzip | bzip2 | xz | z )
            _mimetype=${_mimetype##*[/.-]}
            _mimetype=${_mimetype//ip}
            _basemimetype=$(file -zLb --mime-type "$1")
            ;;
        stream )
            _mimetype=($(file -Lb "$1"))
            [ "${_mimetype[1]}" = "compressed" ] &&
                _basemimetype=$(file -b --mime-type - < <(
                        ${_mimetype,,} -d <"$1")) ||
                _basemimetype=${_mimetype,,}
            _mimetype=${_mimetype,,}
            ;;
        executable )  _mimetype='' _basemimetype='' ;;
        dosexec )     _mimetype='' _basemimetype='exe' ;;
        shellscript ) _mimetype='' _basemimetype='sh' ;;
        * )
            _basemimetype=$_mimetype
            _mimetype=''
            ;;
    esac
    while read -a _line ;do
        if [ "$_line" == "$_basemimetype" ] ;then
            [ "$_line[1]" ] &&
                _basemimetype=${_line[1]} ||
                _basemimetype=${_basemimetype##*[/.-]}
            break
        fi
        done </etc/mime.types
    case ${_basemimetype##*[/.-]} in
        executable ) _basemimetype='' ;;
        shellscript ) _basemimetype='sh' ;;
        dosexec ) _basemimetype='exe' ;;
        * ) ;;
    esac
    [ "$_mimetype" ] && [ "$_basemimetype" != "$_mimetype" ] &&
      printf ${2+-v} $2 "%s.%s" ${_basemimetype##*[/.-]} ${_mimetype##*[/.-]} ||
      printf ${2+-v} $2 "%s" ${_basemimetype##*[/.-]}
}

此函数可以设置稍后可以使用的Bash变量:

(灵感来自@Petersh正确答案):

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

echo "$fullfile -> $filename . $extension"

这里有一些替代建议(主要是awk),包括一些高级用例,比如提取软件包的版本号。

请注意,如果输入稍有不同,其中一些可能会失败,因此任何使用这些输入的人都应验证其预期输入,并根据需要调整正则表达式。

f='/path/to/complex/file.1.0.1.tar.gz'

# Filename : 'file.1.0.x.tar.gz'
    echo "$f" | awk -F'/' '{print $NF}'

# Extension (last): 'gz'
    echo "$f" | awk -F'[.]' '{print $NF}'
    
# Extension (all) : '1.0.1.tar.gz'
    echo "$f" | awk '{sub(/[^.]*[.]/, "", $0)} 1'
    
# Extension (last-2): 'tar.gz'
    echo "$f" | awk -F'[.]' '{print $(NF-1)"."$NF}'

# Basename : 'file'
    echo "$f" | awk '{gsub(/.*[/]|[.].*/, "", $0)} 1'

# Basename-extended : 'file.1.0.1.tar'
    echo "$f" | awk '{gsub(/.*[/]|[.]{1}[^.]+$/, "", $0)} 1'

# Path : '/path/to/complex/'
    echo "$f" | awk '{match($0, /.*[/]/, a); print a[0]}'
    # or 
    echo "$f" | grep -Eo '.*[/]'
    
# Folder (containing the file) : 'complex'
    echo "$f" | awk -F'/' '{$1=""; print $(NF-1)}'
    
# Version : '1.0.1'
    # Defined as 'number.number' or 'number.number.number'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?'

    # Version - major : '1'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f1

    # Version - minor : '0'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f2

    # Version - patch : '1'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f3

# All Components : "path to complex file 1 0 1 tar gz"
    echo "$f" | awk -F'[/.]' '{$1=""; print $0}'
    
# Is absolute : True (exit-code : 0)
    # Return true if it is an absolute path (starting with '/' or '~/'
    echo "$f" | grep -q '^[/]\|^~/'
 

所有用例都使用原始完整路径作为输入,而不依赖中间结果。