ibz*_*ibz 1969 string bash filenames
我想分别获取文件名(没有扩展名)和扩展名.
我到目前为止找到的最佳解决方案是:
NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`
Run Code Online (Sandbox Code Playgroud)
这是错误的,因为如果文件名包含多个.字符,它将不起作用.如果,让我们说,我有a.b.js,它会考虑a和b.js,而不是a.b和js.
它可以在Python中轻松完成
file, ext = os.path.splitext(path)
Run Code Online (Sandbox Code Playgroud)
但是如果可能的话,我宁愿不为此启动Python解释器.
有更好的想法吗?
Pet*_*esh 3313
首先,获取没有路径的文件名:
filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"
Run Code Online (Sandbox Code Playgroud)
或者,您可以专注于路径的最后一个'/'而不是'.' 即使你有不可预测的文件扩展名,它应该工作:
filename="${fullfile##*/}"
Run Code Online (Sandbox Code Playgroud)
您可能需要查看文档:
Jul*_*ano 614
~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz
Run Code Online (Sandbox Code Playgroud)
有关更多详细信息,请参阅Bash手册中的shell参数扩展.
小智 378
通常您已经知道扩展名,因此您可能希望使用:
basename filename .extension
Run Code Online (Sandbox Code Playgroud)
例如:
basename /path/to/dir/filename.txt .txt
Run Code Online (Sandbox Code Playgroud)
我们得到了
filename
Run Code Online (Sandbox Code Playgroud)
sot*_*pme 137
你可以使用POSIX变量的神奇之处:
bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo ${FILENAME%%.*}
somefile
bash-3.2$ echo ${FILENAME%.*}
somefile.tar
Run Code Online (Sandbox Code Playgroud)
有一个警告,如果你的文件名是形式,./somefile.tar.gz那么echo ${FILENAME%%.*}贪婪地删除最长的匹配.,你就有了空字符串.
(您可以使用临时变量解决此问题:
FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}
Run Code Online (Sandbox Code Playgroud)
)
这个网站解释更多.
${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
Run Code Online (Sandbox Code Playgroud)
Doc*_*r J 68
如果文件没有扩展名或没有文件名,那似乎不起作用.这是我正在使用的; 它只使用内置函数并处理更多(但不是全部)病态文件名.
#!/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
Run Code Online (Sandbox Code Playgroud)
以下是一些测试用例:
$ 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 = ""
Bja*_*sen 43
你可以用basename.
例:
$ basename foo-bar.tar.gz .tar.gz
foo-bar
Run Code Online (Sandbox Code Playgroud)
您需要提供带有要删除的扩展名的basename,但是如果您总是执行tar,-z那么您将知道扩展名.tar.gz.
这应该做你想要的:
tar -zxvf $1
cd $(basename $1 .tar.gz)
Run Code Online (Sandbox Code Playgroud)
pax*_*blo 32
pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js
Run Code Online (Sandbox Code Playgroud)
工作正常,所以你可以使用:
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
Run Code Online (Sandbox Code Playgroud)
顺便说一下,这些命令的工作原理如下.
用于NAME替换"."字符后跟任意数量的非"."字符直到行尾的命令,没有任何内容(即,它删除从最后"."到行尾的所有内容,包括在内).这基本上是使用正则表达式欺骗的非贪婪替换.
用于EXTENSION替换任意数量的字符的命令,后跟"."行开头的字符,没有任何内容(即,它删除从行的开头到最后一个点的所有内容,包括在内).这是一个贪婪的替换,这是默认操作.
小智 28
梅伦在博客文章评论中写道:
使用Bash,还${file%.*}可以获取没有扩展名的文件名并${file##*.}单独获取扩展名.那是,
file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"
Run Code Online (Sandbox Code Playgroud)
输出:
filename: thisfile
extension: txt
Run Code Online (Sandbox Code Playgroud)
Cyk*_*ker 26
无需费心awk或者sed甚至perl为这个简单的任务.有一个纯Bash,os.path.splitext()兼容的解决方案,只使用参数扩展.
分裂路径名路径成一对
(root, ext),使得root + ext == path,和分机是空的或用一个周期开始,并且包含至多一个周期.基本名称的前导句点被忽略;splitext('.cshrc')回报('.cshrc', '').
Python代码:
root, ext = os.path.splitext(path)
Run Code Online (Sandbox Code Playgroud)
root="${path%.*}"
ext="${path#"$root"}"
Run Code Online (Sandbox Code Playgroud)
root="${path#.}";root="${path%"$root"}${root%.*}"
ext="${path#"$root"}"
Run Code Online (Sandbox Code Playgroud)
以下是Ignoring leading period实现的测试用例,它应该与每个输入上的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' |
|---------------|-----------|-------|
Run Code Online (Sandbox Code Playgroud)
所有测试都通过了
Som*_*ude 25
您可以使用该cut命令删除最后两个扩展(".tar.gz"部分):
$ echo "foo.tar.gz" | cut -d'.' --complement -f2-
foo
Run Code Online (Sandbox Code Playgroud)
正如Clayton Hughes在评论中指出的那样,这对问题中的实际例子不起作用.所以作为替代方案,我建议使用sed扩展正则表达式,如下所示:
$ echo "mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'
mpc-1.0.1
Run Code Online (Sandbox Code Playgroud)
它的工作原理是无条件地删除最后两个(字母数字)扩展.
[在Anders Lindahl发表评论后再次更新]
hen*_*ber 21
以下是一些替代建议(主要是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 '^[/]\|^~/'
Run Code Online (Sandbox Code Playgroud)
所有用例都使用原始完整路径作为输入,而不依赖于中间结果.
mkl*_*nt0 18
该接受的答案行之有效的典型案例,但在失败边缘的情况下,即:
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'
Run Code Online (Sandbox Code Playgroud)
请注意,输入路径之后的参数是自由选择的,位置变量名称.
要跳过那些之前不感兴趣的变量,指定_(使用丢弃变量$_)或''; 例如,要仅提取文件名根和扩展名,请使用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
Run Code Online (Sandbox Code Playgroud)
执行该功能的测试代码:
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
Run Code Online (Sandbox Code Playgroud)
预期输出 - 注意边缘情况:
.(不被视为后缀的开头)开头的文件名/(尾随/被忽略)结尾的输入路径.作为文件名的输入路径(作为父路径返回).前缀标记的文件名(只有最后一个被认为是后缀):----- /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
Run Code Online (Sandbox Code Playgroud)
小智 17
最小和最简单的解决方案(单线)是:
$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*}) # foo
Run Code Online (Sandbox Code Playgroud)
小智 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"
Run Code Online (Sandbox Code Playgroud)
这就是全部= D.
Ken*_*ler 12
这是唯一对我有用的:
path='folder/other_folder/file.js'
base=${path##*/}
echo ${base%.*}
>> file
Run Code Online (Sandbox Code Playgroud)
这也可以用于字符串插值,但不幸的是你必须base事先设置。
小智 11
您可以强制剪切以显示添加-到字段编号的所有字段和后续字段.
NAME=`basename "$FILE"`
EXTENSION=`echo "$NAME" | cut -d'.' -f2-`
Run Code Online (Sandbox Code Playgroud)
因此,如果FILE是eth0.pcap.gz,则EXTENSION将是pcap.gz
使用相同的逻辑,您还可以使用带有cut的' - '来获取文件名,如下所示:
NAME=`basename "$FILE" | cut -d'.' -f-1`
Run Code Online (Sandbox Code Playgroud)
这适用于没有任何扩展名的文件名.
小智 8
好的,如果我理解正确,这里的问题是如何获得具有多个扩展名的文件的名称和完整扩展名,例如stuff.tar.gz.
这对我有用:
fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}
Run Code Online (Sandbox Code Playgroud)
这将为您stuff提供文件名和.tar.gz扩展名.它适用于任何数量的扩展,包括0.希望这有助于任何有相同问题的人=)
之前的答案没有使用 bash 正则表达式
\n这里是一个纯 bash 解决方案,它将路径分成:
/,当存在时其尾随/太长,所以我没有发布它.该代码旨在处理所有可能的情况,欢迎您尝试。
\n#!/bin/bash\n\nfor path; do\n\n####### the relevant part ######\n\n[[ $path =~ ^(\\.{1,2}|.*/\\.{0,2})$|^(.*/)([^/]+)(\\.[^/]*)$|^(.*/)(.+)$|^(.+)(\\..*)$|^(.+)$ ]]\n\ndirpath=${BASH_REMATCH[1]}${BASH_REMATCH[2]}${BASH_REMATCH[5]}\nfilename=${BASH_REMATCH[3]}${BASH_REMATCH[6]}${BASH_REMATCH[7]}${BASH_REMATCH[9]}\nfilext=${BASH_REMATCH[4]}${BASH_REMATCH[8]}\n\n# dirpath should be non-null\n[[ $dirpath ]] || dirpath=\'.\'\n\n################################\n\nprintf \'%s=%q\\n\' \\\n path "$path" \\\n dirpath "$dirpath" \\\n filename "$filename" \\\n filext "$filext"\n\ndone\nRun Code Online (Sandbox Code Playgroud)\n它是如何工作的?
\n基本上,它确保只有一个子表达式(|在正则表达式中用 分隔)能够捕获输入。因此,您可以串联存储在 中的所有相同类型的捕获组(例如,与目录路径相关的捕获组)BASH_REMATCH,因为最多有一个为非空。
+--------------------------------------------------------+\n| input dirpath filename filext |\n+--------------------------------------------------------+\n\'\' . \'\' \'\'\n. . \'\' \'\'\n.. .. \'\' \'\'\n... . .. .\n.file . .file \'\'\n.file. . .file .\n.file.. . .file. .\n.file.Z . .file .Z\n.file.sh.Z . .file.sh .Z\nfile . file \'\'\nfile. . file .\nfile.. . file. .\nfile.Z . file .Z\nfile.sh.Z . file.sh .Z\ndir/ dir/ \'\' \'\'\ndir/. dir/. \'\' \'\'\ndir/... dir/ .. .\ndir/.file dir/ .file \'\'\ndir/.file. dir/ .file .\ndir/.file.. dir/ .file. .\ndir/.file.Z dir/ .file .Z\ndir/.file.x.Z dir/ .file.x .Z\ndir/file dir/ file \'\'\ndir/file. dir/ file .\ndir/file.. dir/ file. .\ndir/file.Z dir/ file .Z\ndir/file.x.Z dir/ file.x .Z\ndir./. dir./. \'\' \'\'\ndir./... dir./ .. .\ndir./.file dir./ .file \'\'\ndir./.file. dir./ .file .\ndir./.file.. dir./ .file. .\ndir./.file.Z dir./ .file .Z\ndir./.file.sh.Z dir./ .file.sh .Z\ndir./file dir./ file \'\'\ndir./file. dir./ file .\ndir./file.. dir./ file. .\ndir./file.Z dir./ file .Z\ndir./file.x.Z dir./ file.x .Z\ndir// dir// \'\' \'\'\ndir//. dir//. \'\' \'\'\ndir//... dir// .. .\ndir//.file dir// .file \'\'\ndir//.file. dir// .file .\ndir//.file.. dir// .file. .\ndir//.file.Z dir// .file .Z\ndir//.file.x.Z dir// .file.x .Z\ndir//file dir// file \'\'\ndir//file. dir// file .\ndir//file.. dir// file. .\ndir//file.Z dir// file .Z\ndir//file.x.Z dir// file.x .Z\ndir.//. dir.//. \'\' \'\'\ndir.//... dir.// .. .\ndir.//.file dir.// .file \'\'\ndir.//.file. dir.// .file .\ndir.//.file.. dir.// .file. .\ndir.//.file.Z dir.// .file .Z\ndir.//.file.x.Z dir.// .file.x .Z\ndir.//file dir.// file \'\'\ndir.//file. dir.// file .\ndir.//file.. dir.// file. .\ndir.//file.Z dir.// file .Z\ndir.//file.x.Z dir.// file.x .Z\n/ / \'\' \'\'\n/. /. \'\' \'\'\n/.. /.. \'\' \'\'\n/... / .. .\n/.file / .file \'\'\n/.file. / .file .\n/.file.. / .file. .\n/.file.Z / .file .Z\n/.file.sh.Z / .file.sh .Z\n/file / file \'\'\n/file. / file .\n/file.. / file. .\n/file.Z / file .Z\n/file.sh.Z / file.sh .Z\n/dir/ /dir/ \'\' \'\'\n/dir/. /dir/. \'\' \'\'\n/dir/... /dir/ .. .\n/dir/.file /dir/ .file \'\'\n/dir/.file. /dir/ .file .\n/dir/.file.. /dir/ .file. .\n/dir/.file.Z /dir/ .file .Z\n/dir/.file.x.Z /dir/ .file.x .Z\n/dir/file /dir/ file \'\'\n/dir/file. /dir/ file .\n/dir/file.. /dir/ file. .\n/dir/file.Z /dir/ file .Z\n/dir/file.x.Z /dir/ file.x .Z\n/dir./. /dir./. \'\' \'\'\n/dir./... /dir./ .. .\n/dir./.file /dir./ .file \'\'\n/dir./.file. /dir./ .file .\n/dir./.file.. /dir./ .file. .\n/dir./.file.Z /dir./ .file .Z\n/dir./.file.sh.Z /dir./ .file.sh .Z\n/dir./file /dir./ file \'\'\n/dir./file. /dir./ file .\n/dir./file.. /dir./ file. .\n/dir./file.Z /dir./ file .Z\n/dir./file.x.Z /dir./ file.x .Z\n/dir// /dir// \'\' \'\'\n/dir//. /dir//. \'\' \'\'\n/dir//... /dir// .. .\n/dir//.file /dir// .file \'\'\n/dir//.file. /dir// .file .\n/dir//.file.. /dir// .file. .\n/dir//.file.Z /dir// .file .Z\n/dir//.file.x.Z /dir// .file.x .Z\n/dir//file /dir// file \'\'\n/dir//file. /dir// file .\n/dir//file.. /dir// file. .\n/dir//file.Z /dir// file .Z\n/dir//file.x.Z /dir// file.x .Z\n/dir.//. /dir.//. \'\' \'\'\n/dir.//... /dir.// .. .\n/dir.//.file /dir.// .file \'\'\n/dir.//.file. /dir.// .file .\n/dir.//.file.. /dir.// .file. .\n/dir.//.file.Z /dir.// .file .Z\n/dir.//.file.x.Z /dir.// .file.x .Z\n/dir.//file /dir.// file \'\'\n/dir.//file. /dir.// file .\n/dir.//file.. /dir.// file. .\n/dir.//file.Z /dir.// file .Z\n/dir.//file.x.Z /dir.// file.x .Z\n// // \'\' \'\'\n//. //. \'\' \'\'\n//.. //.. \'\' \'\'\n//... // .. .\n//.file // .file \'\'\n//.file. // .file .\n//.file.. // .file. .\n//.file.Z // .file .Z\n//.file.sh.Z // .file.sh .Z\n//file // file \'\'\n//file. // file .\n//file.. // file. .\n//file.Z // file .Z\n//file.sh.Z // file.sh .Z\n//dir/ //dir/ \'\' \'\'\n//dir/. //dir/. \'\' \'\'\n//dir/... //dir/ .. .\n//dir/.file //dir/ .file \'\'\n//dir/.file. //dir/ .file .\n//dir/.file.. //dir/ .file. .\n//dir/.file.Z //dir/ .file .Z\n//dir/.file.x.Z //dir/ .file.x .Z\n//dir/file //dir/ file \'\'\n//dir/file. //dir/ file .\n//dir/file.. //dir/ file. .\n//dir/file.Z //dir/ file .Z\n//dir/file.x.Z //dir/ file.x .Z\n//dir./. //dir./. \'\' \'\'\n//dir./... //dir./ .. .\n//dir./.file //dir./ .file \'\'\n//dir./.file. //dir./ .file .\n//dir./.file.. //dir./ .file. .\n//dir./.file.Z //dir./ .file .Z\n//dir./.file.sh.Z //dir./ .file.sh .Z\n//dir./file //dir./ file \'\'\n//dir./file. //dir./ file .\n//dir./file.. //dir./ file. .\n//dir./file.Z //dir./ file .Z\n//dir./file.x.Z //dir./ file.x .Z\n//dir// //dir// \'\' \'\'\n//dir//. //dir//. \'\' \'\'\n//dir//... //dir// .. .\n//dir//.file //dir// .file \'\'\n//dir//.file. //dir// .file .\n//dir//.file.. //dir// .file. .\n//dir//.file.Z //dir// .file .Z\n//dir//.file.x.Z //dir// .file.x .Z\n//dir//file //dir// file \'\'\n//dir//file. //dir// file .\n//dir//file.. //dir// file. .\n//dir//file.Z //dir// file .Z\n//dir//file.x.Z //dir// file.x .Z\n//dir.//. //dir.//. \'\' \'\'\n//dir.//... //dir.// .. .\n//dir.//.file //dir.// .file \'\'\n//dir.//.file. //dir.// .file .\n//dir.//.file.. //dir.// .file. .\n//dir.//.file.Z //dir.// .file .Z\n//dir.//.file.x.Z //dir.// .file.x .Z\n//dir.//file //dir.// file \'\'\n//dir.//file. //dir.// file .\n//dir.//file.. //dir.// file. .\n//dir.//file.Z //dir.// file .Z\n//dir.//file.x.Z //dir.// file.x .Z\nRun Code Online (Sandbox Code Playgroud)\n正如您所看到的,行为与basename和不同dirname。\n例如basename\xc2\xa0dir/输出dir,而正则表达式将为您提供一个空文件名。.与和相同..,它们被视为目录,而不是文件名。
我用 10000 个 256 个字符的路径对其进行计时,大约花费了 1 秒,而等效的 POSIX shell 解决方案慢了 2 倍,而基于疯狂分叉(循环内的外部调用for)的解决方案至少慢了 60 倍。
备注:没有必要测试包含\\n或其他臭名昭著的字符的路径,因为 bash 的正则表达式引擎以相同的方式处理所有字符。唯一能够打破当前逻辑的字符是/和,它们以当前.意想不到的方式混合或相乘。当我第一次发布我的答案时,我发现了一些必须修复的边界情况;我不能说正则表达式是 100% 防弹的,但它现在应该非常强大。
顺便说一句,这是产生相同输出的 POSIX shell 解决方案:
\n#!/bin/sh\n\nfor path; do\n\n####### the relevant part ######\n\nfullname=${path##*/}\n\ncase $fullname in\n. | ..)\n dirpath="$path"\n filename=\'\'\n filext=\'\'\n ;;\n*)\n dirpath=${path%"$fullname"}\n dirpath=${dirpath:-.} # dirpath should be non-null\n filename=${fullname#.}\n filename="${fullname%"$filename"}${filename%.*}"\n filext=${fullname#"$filename"}\n ;;\nesac\n\n################################\n\nprintf \'%s=%s\\n\' \\\n path "$path" \\\n dirpath "$dirpath" \\\n filename "$filename" \\\n filext "$filext"\n\ndone\nRun Code Online (Sandbox Code Playgroud)\n后记:有几点可能有些人不同意上面代码给出的结果:
\n点文件的特殊情况:原因是点文件是一个 UNIX 概念。
\n.和 的特殊情况..:恕我直言,将它们视为目录似乎是显而易见的,但大多数库不这样做并强制用户对结果进行后处理。
不支持双扩展名:那是因为您需要一个完整的数据库来存储所有有效的双扩展名,最重要的是,因为文件扩展名在 UNIX 中没有任何意义;例如,您可以调用 tar 存档my_tarred_files,这完全没问题,您将能够tar xf my_tarred_files毫无问题。
除了这个Stack Overflow问题的很多好答案之外,我想补充一下:
在Linux和其他unixen下,有一个名为magic的魔术命令file,它通过分析文件的第一个字节来进行文件类型检测.这是一个非常古老的工具,最初用于打印服务器(如果没有创建...我不确定).
file myfile.txt
myfile.txt: UTF-8 Unicode text
file -b --mime-type myfile.txt
text/plain
Run Code Online (Sandbox Code Playgroud)
标准扩展可以在/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
Run Code Online (Sandbox Code Playgroud)
您可以创建一个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##*[/.-]}
}
Run Code Online (Sandbox Code Playgroud)
此函数可以设置一个可以在以后使用的Bash变量:
(这是受@Petesh正确答案的启发):
filename=$(basename "$fullfile")
filename="${filename%.*}"
file2ext "$fullfile" extension
echo "$fullfile -> $filename . $extension"
Run Code Online (Sandbox Code Playgroud)
只需使用 ${parameter%word}
在你的情况下:
${FILE%.*}
Run Code Online (Sandbox Code Playgroud)
如果您想对其进行测试,请执行以下所有操作,只需删除扩展名:
FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
Run Code Online (Sandbox Code Playgroud)
小智 6
从Petesh答案构建,如果只需要文件名,路径和扩展名都可以在一行中删除,
filename=$(basename ${fullname%.*})
Run Code Online (Sandbox Code Playgroud)
小智 5
$ F = "text file.test.txt"
$ echo ${F/*./}
txt
Run Code Online (Sandbox Code Playgroud)
这适用于文件名中的多个点和空格,但是如果没有扩展名,则返回文件名本身.虽然容易检查; 只测试文件名和扩展名是否相同.
当然,这种方法不适用于.tar.gz文件.但是,这可以通过两个步骤处理.如果扩展名是gz,则再次检查以查看是否还有tar扩展名.
小智 5
这是带有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
Run Code Online (Sandbox Code Playgroud)
很大程度上基于@mklement0的优秀,并且充满了随机的、有用的bashisms - 以及这个/其他问题/“该死的互联网”的其他答案......我把它全部包裹在一个稍微更容易理解的地方,我的(或你的)的可重用函数.bash_profile,它负责(我认为)应该是更健壮的版本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
}
Run Code Online (Sandbox Code Playgroud)
用法示例...
SOMEPATH=/path/to.some/.random\ file.gzip
path $SOMEPATH dir # /path/to.some
path $SOMEPATH name # .random file
path $SOMEPATH ext # gzip
path $SOMEPATH fullname # .random file.gzip
path gobbledygook # usage: -bash <path> <dir|name|fullname|ext>
Run Code Online (Sandbox Code Playgroud)
如何在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
Run Code Online (Sandbox Code Playgroud)
注意事项:在最后一个点上分割,这对于其中带有点的文件名效果很好,但对于其中带有点的扩展名效果不好。请参见下面的示例。
用法:
$ 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.
Run Code Online (Sandbox Code Playgroud)
可能有更好的方法可以做到这一点。随时编辑我的答案以改善它。
如果您要处理的扩展名有限,并且您知道所有扩展名,请尝试以下操作:
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
Run Code Online (Sandbox Code Playgroud)
这并不会有警告作为第一个例子,但你必须处理每一个案件,因此它可以根据你多少可以扩展期待更加繁琐。