在Bash中提取文件名和扩展名
我想分别得到文件名(没有扩展名)和扩展名。
到目前为止,我发现的最佳解决scheme是:
NAME=`echo "$FILE" | cut -d'.' -f1` EXTENSION=`echo "$FILE" | cut -d'.' -f2`
这是错误的,因为如果文件名包含多个“。”,则不起作用。 字符。 如果我们说,我有abjs,它会考虑a和b.js ,而不是ab和js 。
这可以很容易地在Python中完成
file, ext = os.path.splitext(path)
但如果可能的话,我宁愿不要为此解雇Python解释器。
任何更好的想法?
首先,获取没有path的文件名:
filename=$(basename "$fullfile") extension="${filename##*.}" filename="${filename%.*}"
或者,您可以专注于path的最后一个“/”而不是“。”。 即使你有不可预知的文件扩展名也应该工作:
filename="${fullfile##*/}"
~% FILE="example.tar.gz" ~% echo "${FILE%%.*}" example ~% echo "${FILE%.*}" example.tar ~% echo "${FILE#*.}" tar.gz ~% echo "${FILE##*.}" gz
有关更多详细信息,请参阅Bash手册中的shell参数扩展 。
通常你已经知道扩展,所以你可能希望使用:
basename filename .extension
例如:
basename /path/to/dir/filename.txt .txt
我们得到了
filename
你可以使用POSIXvariables的魔力:
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%%.*}
会贪婪地删除最长的匹配.
你会有空的string。
(你可以用临时variables来解决这个问题:
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
如果文件没有扩展名,或者没有文件名,这似乎不起作用。 这是我正在使用的; 它只使用内build并处理更多(但不是全部)的病态文件名。
#!/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
这里有一些testing用例:
$ 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 =“文件” ext =“” /home/me/file.tar: dir =“/ home / me /” base =“文件” 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 =“”
你可以使用basename
。
例:
$ basename foo-bar.tar.gz .tar.gz foo-bar
你需要提供基本名称的扩展名,但是如果你总是用-z
执行tar
,那么你知道扩展名是.tar.gz
。
这应该做你想要的:
tar -zxvf $1 cd $(basename $1 .tar.gz)
您可以使用cut
命令删除最后两个扩展名( ".tar.gz"
部分):
$ echo "foo.tar.gz" | cut -d'.' --complement -f2- foo
正如克莱顿·休斯(Clayton Hughes)在评论中指出的那样,这个问题不适用于实际的例子。 所以作为替代scheme,我build议使用sed
和扩展正则expression式,如下所示:
$ echo "mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//' mpc-1.0.1
它的工作原理是无条件删除最后两个(字母数字)扩展名。
[在Anders Lindahl发表评论后再次更新]
梅伦在一篇博文中写道:
使用Bash,还有${file%.*}
来获取没有扩展名的文件名和${file##*.}
来获得扩展名。 那是,
file="thisfile.txt" echo "filename: ${file%.*}" echo "extension: ${file##*.}"
输出:
filename: thisfile extension: txt
这里有一些替代的build议(主要是在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 '^[/]\|^~/'
所有用例都使用原始完整path作为input,而不依赖于中间结果。
pax> echo abjs | sed 's/\.[^.]*$//' ab pax> echo abjs | sed 's/^.*\.//' js
工作正常,所以你可以使用:
pax> FILE=abjs pax> NAME=$(echo "$FILE" | sed 's/\.[^.]*$//') pax> EXTENSION=$(echo "$FILE" | sed 's/^.*\.//') pax> echo $NAME ab pax> echo $EXTENSION js
顺便说一句,这些命令的工作如下。
NAME
的命令会replace"."
字符后跟任意数量的非"."
字符直到行的末尾,没有任何内容(即,它从最后的"."
到行的末尾,包括所有内容)。 这基本上是一个非贪婪的替代使用正则expression式欺骗。
EXTENSION
命令replace任意数量的字符后跟一个"."
字符在行的开头,没有任何东西(例如,它删除了从行首到最后一个点的所有内容)。 这是一个贪婪的替代,这是默认的行为。
我认为如果你只是需要文件的名字,你可以试试这个:
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。
[从单线程修改为通用bash 函数 ,行为现在与dirname
和basename
实用程序一致; 理由补充。]
接受的答案在典型情况下performance良好 ,但在边缘情况下失败 ,即:
- 对于没有扩展名的文件名(在这个答案的其余部分称为后缀 ),
extension=${filename##*.}
返回input文件名,而不是空string。 -
extension=${filename##*.}
不包括最初的.
,违背惯例。- 盲目前瞻
.
不适用于没有后缀的文件名。
- 盲目前瞻
-
filename="${filename%.*}"
将是空string,如果input文件名称以.
并不包含其他内容.
字符(例如.bash_profile
) – 与惯例相反。
———
因此, 涵盖所有边缘情况的强大解决scheme的复杂性需要一个函数 – 请参阅下面的定义; 它可以返回path的所有组件 。
示例调用:
splitPath '/etc/bash.bashrc' dir fname fnameroot suffix # -> $dir == '/etc' # -> $fname == 'bash.bashrc' # -> $fnameroot == 'bash' # -> $suffix == '.bashrc'
请注意,inputpath之后的参数是自由select的位置variables名称 。
要跳过那些没有兴趣的variables,指定_
(使用抛出variables$_
)或者''
; 例如,只提取文件名根目录和扩展名,使用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
testing代码,执行function:
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
预期的输出 – 注意边缘情况:
- 一个没有后缀的文件名
- 以
.
开头的文件名.
( 不考虑后缀的开始) - 以
/
(尾随/
被忽略)结尾的inputpath - inputpath是一个文件名(仅作为父path返回)
- 有一个以上的文件名
.
-prefixed标记(只有最后一个被认为是后缀):
----- /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
您可以强制剪切以显示所有字段和后续字段添加到字段编号。
NAME=`basename "$FILE"` EXTENSION=`echo "$NAME" | cut -d'.' -f2-`
所以如果FILE是eth0.pcap.gz
,那么EXTENSION就是pcap.gz
使用相同的逻辑,您也可以使用' – '来获取文件名,如下所示:
NAME=`basename "$FILE" | cut -d'.' -f-1`
这甚至可以用于没有任何扩展名的文件名。
这个简单的任务不需要麻烦awk
或sed
甚至perl
。 有一个纯粹的Bash, os.path.splitext()
兼容的解决scheme,只使用参数扩展。
参考实现
os.path.splitext(path)
文档os.path.splitext(path)
:
将path名path拆分成一对
(root, ext)
,使得root + ext == path
, ext是空的,或者以句点开始并且至多包含一个句点。 在基本名称的领导阶段被忽略;splitext('.cshrc')
('.cshrc', '')
splitext('.cshrc')
返回('.cshrc', '')
。
Python代码:
root, ext = os.path.splitext(path)
Bash实现
尊重领导阶段
root="${path%.*}" ext="${path#"$root"}"
忽略领先期
root="${path#.}";root="${path%"$root"}${root%.*}" ext="${path#"$root"}"
testing
下面是忽略领导阶段实现的testing用例,它应该匹配每个input的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' | |---------------|-----------|-------|
检测结果
所有testing通过。
好吧,如果我理解正确,这里的问题是如何获得具有多个扩展名的文件的名称和完整扩展名,例如stuff.tar.gz
。
这适用于我:
fullfile="stuff.tar.gz" fileExt=${fullfile#*.} fileName=${fullfile%*.$fileExt}
这会给你stuff
作为扩展名的文件名和.tar.gz
。 它适用于任何数量的扩展,包括0.希望这有助于任何人有相同的问题=)
魔术文件识别
除了这个堆栈溢出问题的很多好的答案,我想补充:
在Linux和其他unixen下,有一个名为file
的魔术命令,它通过分析文件的第一个字节来做文件types检测。 这是一个非常古老的工具,用于打印服务器的初始化(如果没有创build…我不确定)。
file myfile.txt myfile.txt: UTF-8 Unicode text file -b --mime-type myfile.txt text/plain
标准扩展可以在/etc/mime.types
中find(在我的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
你可以创build一个确定右扩展的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##*[/.-]} }
这个函数可以设置一个可以在以后使用的Bashvariables:
(这是来自@Petesh的正确答案):
filename=$(basename "$fullfile") filename="${filename%.*}" file2ext "$fullfile" extension echo "$fullfile -> $filename . $extension"
我使用下面的脚本
$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev foo
$ F = "text file.test.txt" $ echo ${F/*./} txt
这将迎合文件名中的多个点和空格,但是如果没有扩展名,它将返回文件名。 虽然容易检查; 只是testing文件名和扩展名是相同的。
当然,这种方法不适用于.tar.gz文件。 但是,这可以通过两个步骤来处理。 如果扩展名为gz,则再次检查是否还有焦油扩展。
如何提取鱼的文件名和扩展名:
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
这没有作为第一个例子的警告,但你必须处理每一个案件,所以这可能是更乏味的,取决于你可以期待多less扩展。
这里是AWK的代码。 它可以做得更简单。 但是我不擅长AWK。
filename$ ls abc.a.txt abctxt pp-kk.txt filename$ find . -type f | awk -F/ '{print $2}' | rev | awk -F"." '{$1="";print}' | rev | awk 'gsub(" ",".") ,sub(".$", "")' abc.a abc pp-kk filename$ find . -type f | awk -F/ '{print $2}' | awk -F"." '{print $NF}' txt txt txt
最小和最简单的解决scheme(单行)是:
$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*})
foo
一个简单的答案:
要扩展POSIXvariables的答案 ,请注意,你可以做更多有趣的模式。 所以对于这里详细的情况,你可以简单地做到这一点:
tar -zxvf $1 cd ${1%.tar.*}
这将切断.tar的最后一次出现。 <something> 。
更一般地说,如果你想删除最后一次出现。 <something> 。 然后, <something-else>
${1.*.*}
应该工作正常。
上面的答案链接似乎已经死了。 这里有一个很好的解释,你可以从TLDP直接在Bash中进行一些string操作 。
如果只需要文件名,那么path和扩展名都可以在一行中去掉,
filename=$(basename ${fullname%.*})
在很大程度上,基于@ mklement0的优秀,充满了随机,有用的bashisms – 以及其他答案这个/其他问题/“互联网打networking”…我把它包起来一点点,更容易理解,我的(或你的) .bash_profile
可重用的function ,照顾什么(我认为)应该是一个更强大的版本的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 extesionless 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 }
用法示例…
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>
From the answers above, the shortest oneliner to mimic Python's
file, ext = os.path.splitext(path)
presuming your file really does have an extension, is
EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
If you also want to allow empty extensions, this is the shortest I could come up with:
sed -r 's/.+\.(.+)|.*/\1/' # EXTENSION sed -r 's/(.+)\..+|(.*)/\1\2/' # FILENAME
1st line explained: It matches PATH.EXT or ANYTHING and replaces it with EXT. If ANYTHING was matched, the ext group is not captured.
In order to make dir more useful (in the case a local file with no path is specified as input) I did the following:
# Substring from 0 thru pos of filename dir="${fullpath:0:${#fullpath} - ${#filename}}" if [[ -z "$dir" ]]; then dir="./" fi
This allows you to do something useful like add a suffix to the input file basename as:
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"
您可以使用
sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
to get file name and
sed 's/^/./' | rev | cut -d. -f1 | rev
to get extension.
testing用例:
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
Here is the algorithm I used for finding the name and extension of a file when I wrote a Bash script to make names unique when names conflicted with respect to casing.
#! /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
The test run.
$ 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= $
FYI: The complete transliteration program and more test cases can be found here: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0
Using example file /Users/Jonathan/Scripts/bash/MyScript.sh
, this code:
MY_EXT=".${0##*.}" ME=$(/usr/bin/basename "${0}" "${MY_EXT}")
will result in ${ME}
being MyScript
and ${MY_EXT}
being .sh
:
脚本:
#!/bin/bash set -e MY_EXT=".${0##*.}" ME=$(/usr/bin/basename "${0}" "${MY_EXT}") echo "${ME} - ${MY_EXT}"
一些testing:
$ ./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