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

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

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

有更好的主意吗?


当前回答

最小和最简单的解决方案(单行)是:

$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*}) # foo

其他回答

我认为,如果您只需要文件名,可以尝试以下操作:

FULLPATH=/usr/share/X11/xorg.conf.d/50-synaptics.conf

# Remove all the prefix until the "/" character
FILENAME=${FULLPATH##*/}

# Remove all the prefix until the "." character
FILEEXTENSION=${FILENAME##*.}

# Remove a suffix, in our case, the filename. This will return the name of the directory that contains this file.
BASEDIRECTORY=${FULLPATH%$FILENAME}

echo "path = $FULLPATH"
echo "file name = $FILENAME"
echo "file extension = $FILEEXTENSION"
echo "base directory = $BASEDIRECTORY"

这就是全部=D。

Mellen在一篇博客文章中写道:

使用Bash,还有${file%.*}获取不带扩展名的文件名,${file##*.}单独获取扩展名。即,

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

输出:

filename: thisfile
extension: txt

您可以使用

sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-

获取文件名和

sed 's/^/./' | rev | cut -d. -f1  | rev

以获得扩展。

测试用例:

echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename"        | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename"        | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f1  | rev

如果您还想允许空扩展,这是我能想到的最短的:

echo 'hello.txt' | sed -r 's/.+\.(.+)|.*/\1/' # EXTENSION
echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)/\1\2/' # FILENAME

第一行解释道:它匹配PATH.EXT或ANYTHING,并将其替换为EXT。如果匹配了ANYTHNG,则不会捕获EXT组。

这里是一个sed解决方案,它以各种形式提取路径组件,并可以处理大多数边缘情况:

## Enter the input path and field separator character, for example:
## (separatorChar must not be present in inputPath)

inputPath="/path/to/Foo.bar"
separatorChar=":"

## sed extracts the path components and assigns them to output variables

oldIFS="$IFS"
IFS="$separatorChar"
read dirPathWithSlash dirPath fileNameWithExt fileName fileExtWithDot fileExt <<<"$(sed -En '
s/^[[:space:]]+//
s/[[:space:]]+$//
t l1
:l1
s/^([^/]|$)//
t
s/[/]+$//
t l2
:l2
s/^$/filesystem\/\
filesystem/p
t
h
s/^(.*)([/])([^/]+)$/\1\2\
\1\
\3/p
g
t l3
:l3
s/^.*[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\
\2\3\
\3/p
t
s/^.*[/](.+)$/\1/p
' <<<"$inputPath" | tr "\n" "$separatorChar")"
IFS="$oldIFS"

## Results (all use separatorChar=":")

## inputPath        = /path/to/Foo.bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = Foo.bar
## fileName         = Foo
## fileExtWithDot   = .bar
## fileExt          = bar

## inputPath        = /path/to/Foobar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = Foobar
## fileName         = Foobar
## fileExtWithDot   =
## fileExt          =

## inputPath        = /path/to/...bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = ...bar
## fileName         = ..
## fileExtWithDot   = .bar
## fileExt          = bar

## inputPath        = /path/to/..bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = ..bar
## fileName         = .
## fileExtWithDot   = .bar
## fileExt          = bar

## inputPath        = /path/to/.bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = .bar
## fileName         = .bar
## fileExtWithDot   = 
## fileExt          = 

## inputPath        = /path/to/...
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = ...
## fileName         = ...
## fileExtWithDot   =
## fileExt          =

## inputPath        = /path/to/Foo.
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = Foo.
## fileName         = Foo.
## fileExtWithDot   =
## fileExt          =

## inputPath        = / (the root directory)
## dirPathWithSlash = filesystem/
## dirPath          = filesystem
## fileNameWithExt  =
## fileName         =
## fileExtWithDot   =
## fileExt          =

## inputPath        =  (invalid because empty)
## dirPathWithSlash =
## dirPath          =
## fileNameWithExt  =
## fileName         =
## fileExtWithDot   =
## fileExt          =

## inputPath        = Foo/bar (invalid because doesn't start with a forward slash)
## dirPathWithSlash =
## dirPath          =
## fileNameWithExt  =
## fileName         =
## fileExtWithDot   =
## fileExt          =

以下是它的工作原理:

sed解析输入路径,并在单独的行上按顺序打印以下路径组件:

带有尾随斜杠字符的目录路径没有尾随斜杠字符的目录路径带扩展名的文件名不带扩展名的文件名带前导点字符的文件扩展名不带前导点字符的文件扩展名

tr将sed输出转换为上述路径组件的分隔符分隔字符串。

read使用分隔符作为字段分隔符(IFS=“$sseparatorChar”),并将每个路径组件分配给各自的变量。

以下是sed构造的工作原理:

s/^[[:space:]]+/-和s/[[:space:]]+$//去掉任何前导和/或尾随空格字符t l1和:l1为下一个s函数刷新t函数s/^([^/]|$)//和t测试输入路径是否无效(不是以正斜杠开头的路径),在这种情况下,它将所有输出行留空并退出sed命令s/[/]+$//去掉任何尾随斜线t l2和:l2为下一个s函数刷新t函数s/^$/files\/\\[newline]filesystem/p和t测试输入路径由根目录/组成的特殊情况,在这种情况下,它为dirPathWithSlash和dirPath输出行打印文件系统/和文件系统,将所有其他输出行留空,并退出sed命令h将输入路径保存在等待空间中s/^(.*)([/])([^/]+)$/\1\\2\\[newline]\1\\[newline]\3/p打印dirPathWithSlash、dirPath和fileNameWithExt输出行g从保持空间检索输入路径t l3和:l3为下一个s函数刷新t函数s/^.*\[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\\[newline]\2\3\[newline]\3/p,并在存在文件扩展名的情况下打印fileName、fileExtWithDot和fileExt输出行(假设仅由字母数字字符组成),然后退出sed命令s/^.*\[/](.+)$/\1/p在文件扩展名不存在的情况下打印fileName,但不打印fileExtWithDot和fileExt输出行,然后退出sed命令。