我想分别获取文件名(不带扩展名)和扩展名。
我到目前为止找到的最佳解决方案是:
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解释器。
有更好的想法吗?
答案 0 :(得分:3137)
首先,获取没有路径的文件名:
filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"
或者,您可以专注于路径的最后一个'/'而不是'。'即使你有不可预测的文件扩展名,它应该工作:
filename="${fullfile##*/}"
您可能需要查看文档:
答案 1 :(得分:571)
~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz
有关详细信息,请参阅Bash手册中的shell parameter expansion。
答案 2 :(得分:341)
通常您已经知道扩展名,因此您可能希望使用:
basename filename .extension
例如:
basename /path/to/dir/filename.txt .txt
我们得到了
filename
答案 3 :(得分:133)
您可以使用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%%.*}
)
这site解释了更多。
${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
答案 4 :(得分:67)
如果文件没有扩展名或没有文件名,那似乎不起作用。这是我正在使用的;它只使用内置函数并处理更多(但不是全部)病态文件名。
#!/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 = "" /home/me/: dir = "/home/me/" base = "" ext = "" /home/me/file: 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" /home/me/..: dir = "/home/me/" base = ".." ext = "" .: dir = "" base = "." ext = ""
答案 5 :(得分:42)
您可以使用basename
。
示例:
$ basename foo-bar.tar.gz .tar.gz
foo-bar
您确实需要提供带有要删除的扩展名的basename,但是如果您始终使用tar
执行-z
,那么您知道扩展名为.tar.gz
。
这应该做你想要的:
tar -zxvf $1
cd $(basename $1 .tar.gz)
答案 6 :(得分:29)
pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js
工作正常,所以你可以使用:
pax> FILE=a.b.js
pax> NAME=$(echo "$FILE" | sed 's/\.[^.]*$//')
pax> EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
pax> echo $NAME
a.b
pax> echo $EXTENSION
js
顺便说一下,这些命令的工作原理如下。
NAME
的命令替换了"."
字符,后跟任意数量的非"."
字符,直到行尾,没有任何内容(即,它删除了所有内容)最后"."
到最后一行,包括在内)。这基本上是使用正则表达式欺骗的非贪婪替换。
EXTENSION
的命令替换任意数量的字符,后跟行尾的"."
字符,没有任何字符(即,它删除从行开头到最后一行的所有内容)点,包括)。这是一个贪婪的替换,这是默认操作。
答案 7 :(得分:26)
使用Bash,还有${file%.*}
来获取没有扩展名的文件名,${file##*.}
来获取扩展名。也就是说,
file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"
输出:
filename: thisfile
extension: txt
答案 8 :(得分:25)
您可以使用cut
命令删除最后两个扩展名(".tar.gz"
部分):
$ echo "foo.tar.gz" | cut -d'.' --complement -f2-
foo
正如Clayton Hughes在评论中指出的那样,这对问题中的实际例子不起作用。因此,作为替代方案,我建议使用带有扩展正则表达式的sed
,如下所示:
$ echo "mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'
mpc-1.0.1
它的工作原理是无条件地删除最后两个(字母数字)扩展名。
[Anders Lindahl评论后再次更新]
答案 9 :(得分:23)
对于这项简单的任务,无需为awk
或sed
甚至perl
而烦恼。有一个纯Bash,os.path.splitext()
兼容的解决方案只使用参数扩展。
将路径名路径拆分为一对
(root, ext)
,使root + ext == path
和 ext 为空或以句点开头,最多包含一个句点。基本名称的前导句点被忽略;splitext('.cshrc')
会返回('.cshrc', '')
。
Python代码:
root, ext = os.path.splitext(path)
root="${path%.*}"
ext="${path#"$root"}"
root="${path#.}";root="${path%"$root"}${root%.*}"
ext="${path#"$root"}"
以下是忽略前导期实现的测试用例,它应与每个输入上的Python参考实现相匹配。
|---------------|-----------|-------|
|path |root |ext |
|---------------|-----------|-------|
|' .txt' |' ' |'.txt' |
|' .txt.txt' |' .txt' |'.txt' |
|' txt' |' txt' |'' |
|'*.txt.txt' |'*.txt' |'.txt' |
|'.cshrc' |'.cshrc' |'' |
|'.txt' |'.txt' |'' |
|'?.txt.txt' |'?.txt' |'.txt' |
|'\n.txt.txt' |'\n.txt' |'.txt' |
|'\t.txt.txt' |'\t.txt' |'.txt' |
|'a b.txt.txt' |'a b.txt' |'.txt' |
|'a*b.txt.txt' |'a*b.txt' |'.txt' |
|'a?b.txt.txt' |'a?b.txt' |'.txt' |
|'a\nb.txt.txt' |'a\nb.txt' |'.txt' |
|'a\tb.txt.txt' |'a\tb.txt' |'.txt' |
|'txt' |'txt' |'' |
|'txt.pdf' |'txt' |'.pdf' |
|'txt.tar.gz' |'txt.tar' |'.gz' |
|'txt.txt' |'txt' |'.txt' |
|---------------|-----------|-------|
所有测试都通过了。
答案 10 :(得分:20)
以下是一些替代建议(主要在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 '^[/]\|^~/'
所有用例都使用原始完整路径作为输入,而不依赖于中间结果。
答案 11 :(得分:16)
最小和最简单的解决方案(单线)是:
$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*}) # foo
答案 12 :(得分:14)
accepted answer适用于典型个案,但在 edge 个案中失败,即:
extension=${filename##*.}
将返回输入文件名而不是空字符串。extension=${filename##*.}
不包含初始.
,与惯例相反。
.
不适用于没有后缀的文件名。filename="${filename%.*}"
开头并且不包含其他.
个字符(例如.
),则.bash_profile
将为空字符串 - 与惯例相反。 因此,涵盖所有边缘情况的强大解决方案的复杂性需要功能 - 请参阅下面的定义; 可以返回路径的所有组件。
示例电话:
splitPath '/etc/bash.bashrc' dir fname fnameroot suffix
# -> $dir == '/etc'
# -> $fname == 'bash.bashrc'
# -> $fnameroot == 'bash'
# -> $suffix == '.bashrc'
请注意,输入路径后面的参数是自由选择的,位置变量名称
要跳过那些之前不感兴趣的变量,请指定_
(使用丢弃变量$_
)或''
;例如,要仅提取文件名根目录和扩展名,请使用splitPath '/etc/bash.bashrc' _ _ fnameroot extension
。
# SYNOPSIS
# splitPath path varDirname [varBasename [varBasenameRoot [varSuffix]]]
# DESCRIPTION
# Splits the specified input path into its components and returns them by assigning
# them to variables with the specified *names*.
# Specify '' or throw-away variable _ to skip earlier variables, if necessary.
# The filename suffix, if any, always starts with '.' - only the *last*
# '.'-prefixed token is reported as the suffix.
# As with `dirname`, varDirname will report '.' (current dir) for input paths
# that are mere filenames, and '/' for the root dir.
# As with `dirname` and `basename`, a trailing '/' in the input path is ignored.
# A '.' as the very first char. of a filename is NOT considered the beginning
# of a filename suffix.
# EXAMPLE
# splitPath '/home/jdoe/readme.txt' parentpath fname fnameroot suffix
# echo "$parentpath" # -> '/home/jdoe'
# echo "$fname" # -> 'readme.txt'
# echo "$fnameroot" # -> 'readme'
# echo "$suffix" # -> '.txt'
# ---
# splitPath '/home/jdoe/readme.txt' _ _ fnameroot
# echo "$fnameroot" # -> 'readme'
splitPath() {
local _sp_dirname= _sp_basename= _sp_basename_root= _sp_suffix=
# simple argument validation
(( $# >= 2 )) || { echo "$FUNCNAME: ERROR: Specify an input path and at least 1 output variable name." >&2; exit 2; }
# extract dirname (parent path) and basename (filename)
_sp_dirname=$(dirname "$1")
_sp_basename=$(basename "$1")
# determine suffix, if any
_sp_suffix=$([[ $_sp_basename = *.* ]] && printf %s ".${_sp_basename##*.}" || printf '')
# determine basename root (filemane w/o suffix)
if [[ "$_sp_basename" == "$_sp_suffix" ]]; then # does filename start with '.'?
_sp_basename_root=$_sp_basename
_sp_suffix=''
else # strip suffix from filename
_sp_basename_root=${_sp_basename%$_sp_suffix}
fi
# assign to output vars.
[[ -n $2 ]] && printf -v "$2" "$_sp_dirname"
[[ -n $3 ]] && printf -v "$3" "$_sp_basename"
[[ -n $4 ]] && printf -v "$4" "$_sp_basename_root"
[[ -n $5 ]] && printf -v "$5" "$_sp_suffix"
return 0
}
test_paths=(
'/etc/bash.bashrc'
'/usr/bin/grep'
'/Users/jdoe/.bash_profile'
'/Library/Application Support/'
'readme.new.txt'
)
for p in "${test_paths[@]}"; do
echo ----- "$p"
parentpath= fname= fnameroot= suffix=
splitPath "$p" parentpath fname fnameroot suffix
for n in parentpath fname fnameroot suffix; do
echo "$n=${!n}"
done
done
测试执行该功能的代码:
test_paths=(
'/etc/bash.bashrc'
'/usr/bin/grep'
'/Users/jdoe/.bash_profile'
'/Library/Application Support/'
'readme.new.txt'
)
for p in "${test_paths[@]}"; do
echo ----- "$p"
parentpath= fname= fnameroot= suffix=
splitPath "$p" parentpath fname fnameroot suffix
for n in parentpath fname fnameroot suffix; do
echo "$n=${!n}"
done
done
预期输出 - 注意边缘情况:
.
开头的文件名(不被认为是后缀的开头)/
结尾的输入路径(忽略尾随/
).
作为父路径返回).
的文件名 - 带前缀的标记(只有最后一个被认为是后缀):----- /etc/bash.bashrc
parentpath=/etc
fname=bash.bashrc
fnameroot=bash
suffix=.bashrc
----- /usr/bin/grep
parentpath=/usr/bin
fname=grep
fnameroot=grep
suffix=
----- /Users/jdoe/.bash_profile
parentpath=/Users/jdoe
fname=.bash_profile
fnameroot=.bash_profile
suffix=
----- /Library/Application Support/
parentpath=/Library
fname=Application Support
fnameroot=Application Support
suffix=
----- readme.new.txt
parentpath=.
fname=readme.new.txt
fnameroot=readme.new
suffix=.txt
答案 13 :(得分:12)
我认为如果您只需要文件名,可以试试这个:
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.
答案 14 :(得分:10)
您可以强制剪切以显示将-
添加到字段编号的所有字段和后续字段。
NAME=`basename "$FILE"`
EXTENSION=`echo "$NAME" | cut -d'.' -f2-`
因此,如果FILE为eth0.pcap.gz
,则EXTENSION将为pcap.gz
使用相同的逻辑,你也可以使用' - '获取文件名,如下所示:
NAME=`basename "$FILE" | cut -d'.' -f-1`
即使对于没有任何扩展名的文件名也是如此。
答案 15 :(得分:7)
除了这个Stack Overflow问题的很多好答案之外,我想补充一下:
在Linux和其他unixen下,有一个名为file
的 magic 命令,它通过分析文件的第一个字节来进行文件类型检测。这是一个非常古老的工具,最初用于打印服务器(如果没有创建...我不确定)。
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
。也许您必须安装file
实用程序和mime-support
包):
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变量:
(这是受@Petesh正确答案的启发):
filename=$(basename "$fullfile")
filename="${filename%.*}"
file2ext "$fullfile" extension
echo "$fullfile -> $filename . $extension"
答案 16 :(得分:7)
好的,如果我理解正确,这里的问题是如何获取具有多个扩展名的文件的名称和完整扩展名,例如stuff.tar.gz
。
这对我有用:
fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}
这将为您提供stuff
作为文件名,.tar.gz
作为扩展名。它适用于任何数量的扩展,包括0.希望这对任何有相同问题的人都有帮助=)
答案 17 :(得分:6)
我使用以下脚本
$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo
答案 18 :(得分:5)
$ F = "text file.test.txt"
$ echo ${F/*./}
txt
这适用于文件名中的多个点和空格,但是如果没有扩展名,则返回文件名本身。虽然容易检查;只测试文件名和扩展名是否相同。
自然,此方法不适用于.tar.gz文件。但是,这可以通过两个步骤处理。如果扩展名是gz,那么再次检查以查看是否还有tar扩展名。
答案 19 :(得分:4)
以下是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
答案 20 :(得分:4)
如何在fish中提取文件名和扩展名:
function split-filename-extension --description "Prints the filename and extension"
for file in $argv
if test -f $file
set --local extension (echo $file | awk -F. '{print $NF}')
set --local filename (basename $file .$extension)
echo "$filename $extension"
else
echo "$file is not a valid file"
end
end
end
警告:最后一个点上的拆分,适用于带有点的文件名,但不适用于包含点的扩展名。见下面的例子。
<强>用法:强>
$ split-filename-extension foo-0.4.2.zip bar.tar.gz
foo-0.4.2 zip # Looks good!
bar.tar gz # Careful, you probably want .tar.gz as the extension.
可能有更好的方法来做到这一点。随意编辑我的答案以改进它。
如果您将要处理的扩展程序有限且您知道所有扩展程序,请尝试以下操作:
switch $file
case *.tar
echo (basename $file .tar) tar
case *.tar.bz2
echo (basename $file .tar.bz2) tar.bz2
case *.tar.gz
echo (basename $file .tar.gz) tar.gz
# and so on
end
不这是第一个例子,但是您必须处理每个案例,因此根据您可以预期的扩展数量,它可能会更加繁琐。
答案 21 :(得分:3)
只需使用${parameter%word}
在你的情况下:
${FILE%.*}
如果您想测试它,以下所有工作,只需删除扩展程序:
FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
答案 22 :(得分:3)
从Petesh回答构建,如果只需要文件名, 路径和扩展都可以在一行中删除,
filename=$(basename ${fullname%.*})
答案 23 :(得分:2)
如果您还想允许空扩展程序,这是我能提出的最短内容:
echo 'hello.txt' | sed -r 's/.+\.(.+)|.*/\1/' # EXTENSION
echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)/\1\2/' # FILENAME
第一行解释:它匹配PATH.EXT或ANYTHING并用EXT替换它。如果ANYTHING匹配,则不会捕获ext组。
答案 24 :(得分:2)
很大程度上取决于@ mklement0的优秀,充满了随机,有用的 bashisms - 以及对此/其他问题/“互联网”的其他答案......我把它包裹起来为我的(或你的).bash_profile
做一点点,稍微易于理解,可重复使用的功能来处理(我认为)应该是{{1}的更强大的版本} / dirname
/ 你有什么 ..
basename
用法示例......
function path { SAVEIFS=$IFS; IFS="" # stash IFS for safe-keeping, etc.
[[ $# != 2 ]] && echo "usage: path <path> <dir|name|fullname|ext>" && return # demand 2 arguments
[[ $1 =~ ^(.*/)?(.+)?$ ]] && { # regex parse the path
dir=${BASH_REMATCH[1]}
file=${BASH_REMATCH[2]}
ext=$([[ $file = *.* ]] && printf %s ${file##*.} || printf '')
# edge cases for extensionless files and files like ".nesh_profile.coffee"
[[ $file == $ext ]] && fnr=$file && ext='' || fnr=${file:0:$((${#file}-${#ext}))}
case "$2" in
dir) echo "${dir%/*}"; ;;
name) echo "${fnr%.*}"; ;;
fullname) echo "${fnr%.*}.$ext"; ;;
ext) echo "$ext"; ;;
esac
}
IFS=$SAVEIFS
}
答案 25 :(得分:2)
一个简单的答案:
要扩展POSIX variables answer,请注意您可以执行更多有趣的模式。因此,对于此处详述的案例,您可以简单地执行此操作:
tar -zxvf $1
cd ${1%.tar.*}
这会切断最后一次出现的.tar。&lt; something&gt; 。
更一般地说,如果你想删除最后一次出现。&lt; something&gt; 。&lt; something-else&gt; 那么
${1.*.*}
应该可以正常工作。
上述答案的链接似乎已经死亡。 Here's a great explanation of a bunch of the string manipulation you can do directly in Bash, from TLDP
答案 26 :(得分:1)
使用示例文件/Users/Jonathan/Scripts/bash/MyScript.sh
,此代码:
MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")
将导致${ME}
MyScript
和${MY_EXT}
为.sh
:
脚本:
#!/bin/bash
set -e
MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")
echo "${ME} - ${MY_EXT}"
一些测试:
$ ./MyScript.sh
MyScript - .sh
$ bash MyScript.sh
MyScript - .sh
$ /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh
$ bash /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh
答案 27 :(得分:1)
恕我直言,最好的解决方案已经给出(使用shell参数扩展),并且目前是最佳的解决方案。
但是我添加了一个仅使用dumbs命令的命令,它效率不高,而且没有人认真使用过:
FILENAME=$(echo $FILE | cut -d . -f 1-$(printf $FILE | tr . '\n' | wc -l))
EXTENSION=$(echo $FILE | tr . '\n' | tail -1)
添加了只是为了娱乐:-)
答案 28 :(得分:1)
从上面的答案中,最短的oneliner来模仿Python的
file, ext = os.path.splitext(path)
假设您的文件确实有扩展名,是
EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
答案 29 :(得分:0)
这是我用来查找文件的名称和扩展名的算法,当我编写一个Bash脚本,以便在名称与套管冲突时使名称唯一。
#! /bin/bash
#
# Finds
# -- name and extension pairs
# -- null extension when there isn't an extension.
# -- Finds name of a hidden file without an extension
#
declare -a fileNames=(
'.Montreal'
'.Rome.txt'
'Loundon.txt'
'Paris'
'San Diego.txt'
'San Francisco'
)
echo "Script ${0} finding name and extension pairs."
echo
for theFileName in "${fileNames[@]}"
do
echo "theFileName=${theFileName}"
# Get the proposed name by chopping off the extension
name="${theFileName%.*}"
# get extension. Set to null when there isn't an extension
# Thanks to mklement0 in a comment above.
extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
# a hidden file without extenson?
if [ "${theFileName}" = "${extension}" ] ; then
# hidden file without extension. Fixup.
name=${theFileName}
extension=""
fi
echo " name=${name}"
echo " extension=${extension}"
done
试运行。
$ config/Name\&Extension.bash
Script config/Name&Extension.bash finding name and extension pairs.
theFileName=.Montreal
name=.Montreal
extension=
theFileName=.Rome.txt
name=.Rome
extension=.txt
theFileName=Loundon.txt
name=Loundon
extension=.txt
theFileName=Paris
name=Paris
extension=
theFileName=San Diego.txt
name=San Diego
extension=.txt
theFileName=San Francisco
name=San Francisco
extension=
$
仅供参考:完整的音译程序和更多测试用例可在此处找到: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0
答案 30 :(得分:0)
这是一个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="$separatorChar"
),并将每个路径组件分配给其各自的变量。
以下是 sed
构造的工作原理:
s/^[[:space:]]+//
和 s/[[:space:]]+$//
删除所有前导和/或尾随空白字符t l1
和 :l1
刷新下一个t
功能的s
功能s/^([^/]|$)//
和 t
测试无效的输入路径(不以正斜杠开头),在这种情况下,将所有输出行留空并退出sed
命令s/[/]+$//
删除任何尾随斜杠t l2
和 :l2
刷新下一个t
功能的s
功能s/^$/filesystem\/\\[newline]filesystem/p
和 t
测试输入路径由根目录 / 组成的特殊情况,在这种情况下,它为 dirPathWithSlash 和 dirPath 输出行打印文件系统/ 和文件系统,保留所有其他输出行空白,退出 sed 命令h
将输入路径保存在保留空间s/^(.*)([/])([^/]+)$/\1\2\\[newline]\1\\[newline]\3/p
打印 dirPathWithSlash , dirPath 和 fileNameWithExt 输出行g
从保留空间t l3
和 :l3
刷新下一个t
功能的s
功能s/^.*\[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\\[newline]\2\3\\[newline]\3/p
和 t
打印 fileName , fileExtWithDot 和< strong> fileExt 输出行,用于存在文件扩展名的情况(假设仅包含字母数字字符),然后退出sed
命令s/^.*\[/](.+)$/\1/p
打印 fileName ,但不打印 fileExtWithDot 和 fileExt 输出行如果文件扩展名不存在,则退出sed
命令。答案 31 :(得分:0)
您可以使用
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
答案 32 :(得分:0)
为了使dir更有用(在没有路径的本地文件被指定为输入的情况下),我做了以下内容:
# Substring from 0 thru pos of filename
dir="${fullpath:0:${#fullpath} - ${#filename}}"
if [[ -z "$dir" ]]; then
dir="./"
fi
这使您可以执行一些有用的操作,例如在输入文件basename中添加后缀:
outfile=${dir}${base}_suffix.${ext}
testcase: foo.bar
dir: "./"
base: "foo"
ext: "bar"
outfile: "./foo_suffix.bar"
testcase: /home/me/foo.bar
dir: "/home/me/"
base: "foo"
ext: "bar"
outfile: "/home/me/foo_suffix.bar"
答案 33 :(得分:0)
也许tar
中有一个选项来执行此操作;你检查那个男人了吗?否则,您可以使用Bash string expansion:
test="mpc-1.0.1.tar.gz"
noExt="${test/.tar.gz/}" # Remove the string '.tar.gz'
echo $noExt
答案 34 :(得分:0)
这是唯一为我工作的人:
path='folder/other_folder/file.js'
base=${path##*/}
echo ${base%.*}
>> file
这也可以用于字符串插值,但是不幸的是,您必须事先设置base
。
答案 35 :(得分:-1)
您还可以使用for
循环和tr
从路径中提取文件名...
for x in `echo $path | tr "/" " "`; do filename=$x; done
tr
用空格替换路径中的所有“ / ”分隔符,以便创建字符串列表,for
循环扫描它们,留下最后一个filename
变量。
答案 36 :(得分:-2)
简单的bash one liner。我用它来删除 pwd
中所有文件的 rst 扩展程序for each in `ls -1 *.rst`
do
a=$(echo $each | wc -c)
echo $each | cut -c -$(( $a-5 )) >> blognames
done
它做什么?
1)ls -1 *.rst
将在新行中列出stdout上的所有文件(尝试)。
2)echo $each | wc -c
计算每个文件名中的字符数。
3)echo $each | cut -c -$(( $a-5 ))
最多选择4个字符,即.rst
。