Extract filename and extension in Bash
我想分别获取文件名(不带扩展名)和扩展名。
迄今为止,我发现的最佳解决方案是:
1 2 | NAME=`echo"$FILE" | cut -d'.' -f1` EXTENSION=`echo"$FILE" | cut -d'.' -f2` |
这是错误的,因为如果文件名包含多个
它可以很容易地用python完成
1 | file, ext = os.path.splitext(path) |
但如果可能的话,我不想为此而启动Python解释器。
有更好的主意吗?
首先,获取不带路径的文件名:
1 2 3 | filename=$(basename --"$fullfile") extension="${filename##*.}" filename="${filename%.*}" |
或者,您可以将焦点放在路径的最后一个"/",而不是".",即使您有不可预知的文件扩展名,它也可以工作:
1 | filename="${fullfile##*/}" |
您可能需要检查文档:
- 在Web上的"3.5.3 Shell参数扩展"部分
- 在bash手册页的"参数扩展"部分中
1 2 3 4 5 6 7 8 9 | ~% FILE="example.tar.gz" ~% echo"${FILE%%.*}" example ~% echo"${FILE%.*}" example.tar ~% echo"${FILE#*.}" tar.gz ~% echo"${FILE##*.}" gz |
有关更多详细信息,请参见bash手册中的shell参数扩展。
通常您已经知道扩展名,因此您可能希望使用:
1 | basename filename .extension |
例如:
1 | basename /path/to/dir/filename.txt .txt |
我们得到
1 | filename |
你可以使用魔法posix变量:
1 2 3 4 5 | bash-3.2$ FILENAME=somefile.tar.gz bash-3.2$ echo ${FILENAME%%.*} somefile bash-3.2$ echo ${FILENAME%.*} somefile.tar |
有一个警告,如果你的文件在你的形式,然后删除从
(你可以在一个工作是临时变量:
1 2 3 | FULL_FILENAME=$FILENAME FILENAME=${FULL_FILENAME##*/} echo ${FILENAME%%.*} |
)
本网站介绍了更多。
1 2 3 4 5 6 7 8 | ${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 |
如果文件没有扩展名或文件名,这似乎不起作用。这是我正在使用的;它只使用内置文件,处理更多(但不是全部)病理文件名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/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: \tdir = "$dir" \tbase = "$base" \text = "$ext"" done |
下面是一些测试案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | $ 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 ="" |
您可以使用
例子:
1 2 | $ basename foo-bar.tar.gz .tar.gz foo-bar |
您需要为basename提供要删除的扩展名,但是如果您总是使用
这应该是您想要的:
1 2 | tar -zxvf $1 cd $(basename $1 .tar.gz) |
1 2 3 4 | pax> echo a.b.js | sed 's/\.[^.]*$//' a.b pax> echo a.b.js | sed 's/^.*\.//' js |
工作正常,所以您可以使用:
1 2 3 4 5 6 7 | 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 |
顺便说一下,这些命令的工作原理如下。
梅伦在博客上发表评论:
使用bash,还可以使用
1 2 3 | file="thisfile.txt" echo"filename: ${file%.*}" echo"extension: ${file##*.}" |
输出:
1 2 | filename: thisfile extension: txt |
你可以使用
1 2 | $ echo"foo.tar.gz" | cut -d'.' --complement -f2- foo |
他指出,由休斯(克莱顿评论,这将需要服务的例子中的实际问题。这是一个
1 2 | $ echo"mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//' mpc-1.0.1 |
它由除最后两unconditionally(α值)扩展。
[更新]后又如何从不同的林达尔
对于这个简单的任务,不需要为
Split the pathname path into a pair
(root, ext) such thatroot + ext == path , and ext is empty or begins with a period and contains at most one period. Leading periods on the basename are ignored;splitext('.cshrc') returns('.cshrc', '') .
Python代码:
1 | root, ext = os.path.splitext(path) |
bash实现表彰领导时期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | root="${path%.*}" ext="${path#"$root <div class="suo-content">[collapse title=""]<ul><li>不,<wyn>text.tar.gz</wyn>的基本文件名应该是<wyn>text</wyn>,扩展名应该是<wyn>.tar.gz</wyn>。</li><li>@正如我所说,这里的解决方案与Python中的<wyn>os.path.splitext</wyn>实现相匹配。对于可能有争议的输入,该实现是否健全是另一个主题。</li></ul>[/collapse]</div><hr><P>这里有一些可选的建议(主要在<wyn>awk</wyn>中),包括一些高级用例,比如提取软件包的版本号。</P>[cc lang="bash"]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 '^[/]\|^~/' |
所有用例都使用原始完整路径作为输入,而不依赖于中间结果。
Smallest and simplest solution (in single line) is:
1 2 | $ file=/blaabla/bla/blah/foo.txt echo $(basename ${file%.*}) # foo |
在回答接受作品很典型的案例,但在失败的边缘的情况下,即:
- 扩展(称为文件名后缀为不在本
extension=${filename##*.} 其余的答案),而当输入文件名返回空字符串。 - 不包含
extension=${filename##*.} 首次. ,违反公约。- blindly prepending
. 将不会工作,没有后缀的文件名。
- blindly prepending
filename="${filename%.*}" 想是空字符串,如果输入文件的名称和包含没有进一步起飞和. . 人物(例如,20.bash_profile )会议。
---------
因此,复杂性的鲁棒性解决方案A边缘覆盖所有电话的功能。其定义下面的湖;它可以返回所有组件的路径。
呼叫的例子:
1 2 3 4 5 | splitPath '/etc/bash.bashrc' dir fname fnameroot suffix # -> $dir == '/etc' # -> $fname == 'bash.bashrc' # -> $fnameroot == 'bash' # -> $suffix == '.bashrc' |
注意的是参数自由选择后输入路径,定位变量的名字。一个变量的兴趣是跳跃在那些需要来指定是,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | # 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 |
测试代码的功能,包括:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
价格说明:边缘。
- 一个没有后缀的文件名
- 一个文件名(不被视
. 启动开始的后缀) - 在输入路径
/ (尾是以/ 忽略) - 这是一个在输入路径(文件名是为只读
. 返回父路径) - a filename是安切洛蒂。
. 字头令牌(只被认为是最后的后缀):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ----- /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 |
我认为,如果您只需要文件名,可以尝试以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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。
您可以强制剪切以显示所有字段以及向字段编号添加
1 2 | NAME=`basename"$FILE"` EXTENSION=`echo"$NAME" | cut -d'.' -f2-` |
因此,如果文件是
使用相同的逻辑,还可以使用"-"和CUT提取文件名,如下所示:
1 | NAME=`basename"$FILE" | cut -d'.' -f-1` |
这甚至适用于没有任何扩展名的文件名。
好吧,如果我理解正确的话,这里的问题是如何获得具有多个扩展名的文件的名称和完整扩展名,例如
这对我很有用:
1 2 3 | fullfile="stuff.tar.gz" fileExt=${fullfile#*.} fileName=${fullfile%*.$fileExt} |
这将为您提供文件名为
魔术文件识别
在添加到焊料良好的答案在本栈 ;溢出的问题,我想补充一点:
在Linux和其他unixen命名,有一个神奇的
1 2 3 4 5 | file myfile.txt myfile.txt: UTF-8 Unicode text file -b --mime-type myfile.txt text/plain |
标准的扩展,可以发现在我
1 2 | grep $( file -b --mime-type myfile.txt ) </etc/mime.types text/plain asc txt text pot brf srt |
你可以创建一个bash函数定权的延伸。有一个小的(不完美)的样品。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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)
1 2 3 4 5 | filename=$(basename"$fullfile") filename="${filename%.*}" file2ext"$fullfile" extension echo"$fullfile -> $filename . $extension" |
我用下面的脚本
1 2 | $ echo"foo.tar.gz"|rev|cut -d"." -f3-|rev foo |
1 2 3 | $ F ="text file.test.txt" $ echo ${F/*./} txt |
这可以处理文件名中的多个点和空格,但是如果没有扩展名,则返回文件名本身。但检查起来很容易;只需测试文件名和扩展名是否相同。
当然,这个方法不适用于.tar.gz文件。然而,这可以通过两个步骤来处理。如果扩展名是gz,那么再次检查是否还有tar扩展名。
如何在fish中提取文件名和扩展名:
1 2 3 4 5 6 7 8 9 10 11 | 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 |
注意:在最后一个点上进行拆分,这对包含点的文件名很好,但对于包含点的扩展名不太好。见下面的例子。
用途:
1 2 3 | $ 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. |
有更好的方法可以做到这一点。请随意编辑我的答案以改进它。
如果您要处理的扩展集有限,并且您了解所有扩展集,请尝试以下操作:
1 2 3 4 5 6 7 8 9 | 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 |
这并不是第一个例子中的警告,但是您必须处理每一个案例,这样根据您期望的扩展数量,它可能会更加繁琐。
这是带锥子的代码。它可以做得更简单。但我的锥子不好。
1 2 3 4 5 6 7 8 9 10 | 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 |
只需使用
在你的情况下:
1 | ${FILE%.*} |
如果要测试它,请执行以下所有操作,只需删除扩展名:
1 2 3 | FILE=abc.xyz; echo ${FILE%.*}; FILE=123.abc.xyz; echo ${FILE%.*}; FILE=abc; echo ${FILE%.*}; |
一个简单的答案:
要扩展POSIX变量的答案,请注意,您可以执行更有趣的模式。因此,对于这里详细介绍的案例,您可以简单地执行以下操作:
1 2 | tar -zxvf $1 cd ${1%.tar.*} |
这将切断最后一次出现的.tar.
更一般地说,如果您想删除最后一次出现的。
1 | ${1.*.*} |
应该工作得很好。
上述答案的链接似乎已失效。这里有一个很好的解释,您可以直接在bash中从tldp执行一系列字符串操作。
基于上的开关mklement0的优秀学院",与chock全随机,bashisms -有用的其他工作人员在其他问题的答案"那该死的/ /网络"……我在一个小化妆包,这一切,似乎更多的comprehensible,可重复使用的功能,我(或你的任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 } |
使用的例子。
1 2 3 4 5 6 | 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> |
如果您还想允许空扩展,这是我能想到的最短的:
1 2 | echo 'hello.txt' | sed -r 's/.+\.(.+)|.*/\1/' # EXTENSION echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)/\1\2/' # FILENAME |
第一行解释说:它与path.ext或任何内容匹配,并将其替换为ext。如果匹配了任何内容,则不会捕获ext组。
根据petesh answer构建,如果只需要文件名,路径和扩展都可以在一条线上剥离,
1 | filename=$(basename ${fullname%.*}) |
从上面的答案来看,最短的一行程序可以模仿python的
1 | file, ext = os.path.splitext(path) |
假设您的文件确实有扩展名,是
1 | EXT="${PATH##*.}"; FILE=$(basename"$PATH" .$EXT) |
imho已经给出了最好的解决方案(使用shell参数扩展),目前是最佳的解决方案。
但是,我添加了一个只使用哑铃命令的命令,这是不高效的,任何严肃的人都不应该使用它:
1 2 3 4 | FILENAME=$(echo $FILE | cut -d . -f 1-$(printf $FILE | tr . ' ' | wc -l)) EXTENSION=$(echo $FILE | tr . ' ' | tail -1) |
添加只是为了好玩:—)
也许有一个选项,在
1 2 3 | test="mpc-1.0.1.tar.gz" noExt="${test/.tar.gz/}" # Remove the string '.tar.gz' echo $noExt |
这是我在写bash脚本时用来查找文件名和扩展名的算法,当名称与大小写冲突时,可以使名称唯一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #! /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 |
测试运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | $ 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/avole-conflicts-code.zip?DL=0
使用示例文件
1 2 | MY_EXT=".${0##*.}" ME=$(/usr/bin/basename"${0}""${MY_EXT}") |
将导致
脚本:
1 2 3 4 5 6 7 | #!/bin/bash set -e MY_EXT=".${0##*.}" ME=$(/usr/bin/basename"${0}""${MY_EXT}") echo"${ME} - ${MY_EXT}" |
一些测试:
1 2 3 4 5 6 7 8 9 10 11 | $ ./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 |
下面是一个SED解决方案,它以各种形式提取路径组件,并可以处理大多数边缘情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | ## 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" ""$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为下一个s函数刷新t函数
- S/^([^/]$)//和T测试无效的输入路径(不以正斜杠开头的路径),在这种情况下,它将所有输出行留空并退出SED命令
- s/[/]+$//删除任何尾随斜杠
- t l2和:l2为下一个s函数刷新t函数
- s/^$/filesystem/[newline]filesystem/p和t测试输入路径由根目录/组成的特殊情况,在这种情况下,它用斜杠和dirpath输出行打印dirpath的filesystem/和filesystem,将所有其他输出行留空,并退出sed命令。
- H将输入路径保存在保留空间中
- S/^(.*)([/])([^/]+)$/12[换行符].1[换行符].3/P用斜杠、dirpath和文件名打印dirpathwithext输出行
- G从保留空间检索输入路径
- t l3和:l3为下一个s函数刷新t函数
- S/^.*[/]([^/]+)([.])([A-ZA-Z0-9]+)$/1[newline]23[newline]3/P和T为存在文件扩展名的情况打印文件名、fileextwithdot和fileext输出行(假定仅由字母数字字符组成),然后退出SED命令
- s/^.*[/](.+)$/1/p打印文件名,但不打印文件扩展名不存在的fileextwithdot和fileext输出行,然后退出sed命令
为了让你更有用(案例A本地文件路径指定为输入一个没有)我做下面的:
1 2 3 4 5 | # Substring from 0 thru pos of filename dir="${fullpath:0:${#fullpath} - ${#filename}}" if [[ -z"$dir" ]]; then dir="./" fi |
这允许你做一些有用的类添加一个输入文件的基本文件名后缀为:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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" |
你可以使用
1 | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2- |
获取文件名和
1 | sed 's/^/./' | rev | cut -d. -f1 | rev |
以获得扩展。
测试用例:
1 2 3 4 5 6 | 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 |
还可以使用for循环和tr从路径中提取文件名…
1 | for x in `echo $path | tr"/"""`; do filename=$x; done |
tr将路径中的所有/分隔符替换为空格,从而生成字符串列表,for循环将扫描这些字符串,并将最后一个字符串保留在文件名变量中。
一个简单的bash一行程序。我用这个从pwd中的所有文件中删除rst扩展名
1 2 3 4 5 | for each in `ls -1 *.rst` do a=$(echo $each | wc -c) echo $each | cut -c -$(( $a-5 )) >> blognames done |
它做什么?
1)
2)
3)