仅从 Bash 脚本中的路径获取文件名 [重复]

Posted

技术标签:

【中文标题】仅从 Bash 脚本中的路径获取文件名 [重复]【英文标题】:Get just the filename from a path in a Bash script [duplicate] 【发布时间】:2011-03-22 17:35:00 【问题描述】:

我如何只获得没有扩展名和路径的文件名?

以下内容没有给我扩展名,但我仍然附有路径:

source_file_filename_no_ext=$source_file%.*

【问题讨论】:

echo $(basename "$filepath%.*") $(basename -a -s .ext /path/to/file.ext) 【参考方案1】:

许多类 UNIX 操作系统都有一个 basename 可执行文件,用于非常相似的目的(dirname 用于路径):

pax> full_name=/tmp/file.txt
pax> base_name=$(basename $full_name)
pax> echo $base_name
file.txt

不幸的是,这只是为您提供了文件名,包括扩展名,因此您还需要找到一种方法来删除它。

所以,既然你无论如何都必须这样做,你不妨找到一种方法,可以去除路径扩展。

一种方法(这是bash-only 解决方案,不需要其他可执行文件):

pax> full_name=/tmp/xx/file.tar.gz
pax> xpath=$full_name%/* 
pax> xbase=$full_name##*/
pax> xfext=$xbase##*.
pax> xpref=$xbase%.*
pax> echo "path='$xpath', pref='$xpref', ext='$xfext'"

path='/tmp/xx', pref='file.tar', ext='gz'

那个小 sn-p 设置了xpath(文件路径)、xpref(文件前缀,您具体要求的内容)和xfext(文件扩展名)。

【讨论】:

我知道上面的 bash 与此有关。我只是不知道关键词是什么。我想将路径名、文件名和扩展名分成不同的变量。 如果要获取路径使用: path=$(echo $filename | sed -e 's/\/[^\/]*$/\//') 如果要获取扩展名:ext=$(echo $filename | sed -e 's/[^\.]*\.//') @Keith:对于路径名,使用path=$(dirname $filename);本身并没有提供扩展名的命令,但@paxdiablo 向您展示了 shell 如何做到这一点。 您可以在 参数扩展 下的manual 中阅读有关仅 bash 解决方案的更多信息。 @Startec,因为这就是您运行可执行文件(如basname)、捕获其输出并在bash 命令中使用该输出的方式,就好像您已明确键入它一样。它类似于使用反引号,但在必要时更易于嵌套。【参考方案2】:
$ source_file_filename_no_ext=$source_file%.*
$ echo $source_file_filename_no_ext##*/

【讨论】:

我只得到一个带有扩展名的通配符。 我更喜欢这个,因为它可以在纯 bash 中完成,而无需运行任何其他可执行文件。但是,这里有两个步骤,IMO 只需要一个,就像标记副本中当前投票最多的答案一样:filename="$fileNameWithPath##*/"【参考方案3】:

这是从路径获取文件名的简单方法:

echo "$PATH" | rev | cut -d"/" -f1 | rev

要删除您可以使用的扩展名,假设文件名只有一个点(扩展点):

cut -d"." -f1

【讨论】:

这不是一个好的假设,并且有专门设计的工具和命令可以正确地做到这一点。 另外,我不推荐使用变量名PATH,因为这会与系统的PATH变量冲突【参考方案4】:

basenamedirname 解决方案更方便。这些是替代命令:

FILE_PATH="/opt/datastores/sda2/test.old.img"
echo "$FILE_PATH" | sed "s/.*\///"

这会返回test.old.img,就像basename一样。

这是不带扩展名的盐文件名:

echo "$FILE_PATH" | sed -r "s/.+\/(.+)\..+/\1/"

它返回test.old

下面的语句给出了完整的路径,如dirname 命令。

echo "$FILE_PATH" | sed -r "s/(.+)\/.+/\1/"

它返回/opt/datastores/sda2

【讨论】:

酷,如果有参数呢? 如果您的路径只是 file.txt,这将不起作用。你的第二个代码截图应该是sed -r "s/(.+\/)?(.+)\..+/\2/ 我已经测试过@Cardin。似乎有效。 FILE_PATH="file.txt"; echo "$FILE_PATH" | sed "s/.*\///"; echo "$FILE_PATH" | sed -r "s/.+\/(.+)\..+/\1/"; echo "$FILE_PATH" | sed -r "s/(.+)\/.+/\1/"; 返回 3 次 file.txt @FıratKÜÇÜK 你说得对,我一定是把它和别的东西搞混了。很抱歉!【参考方案5】:

更多替代选项,因为正则表达式 (regi ?) 很棒!

这是一个简单的正则表达式来完成这项工作:

 regex="[^/]*$"

示例(grep):

 FP="/hello/world/my/file/path/hello_my_filename.log"
 echo $FP | grep -oP "$regex"
 #Or using standard input
 grep -oP "$regex" <<< $FP

示例(awk):

 echo $FP | awk 'match($1, "$regex",a)ENDprint a[0]
 #Or using stardard input
 awk 'match($1, "$regex",a)ENDprint a[0] <<< $FP

如果您需要更复杂的正则表达式: 例如,您的路径被包裹在一个字符串中。

 StrFP="my string is awesome file: /hello/world/my/file/path/hello_my_filename.log sweet path bro."

 #this regex matches a string not containing / and ends with a period 
 #then at least one word character 
 #so its useful if you have an extension

 regex="[^/]*\.\w1,"

 #usage
 grep -oP "$regex" <<< $StrFP

 #alternatively you can get a little more complicated and use lookarounds
 #this regex matches a part of a string that starts with /  that does not contain a / 
 ##then uses the lazy operator ? to match any character at any amount (as little as possible hence the lazy)
 ##that is followed by a space
 ##this allows use to match just a file name in a string with a file path if it has an exntension or not
 ##also if the path doesnt have file it will match the last directory in the file path 
 ##however this will break if the file path has a space in it.

 regex="(?<=/)[^/]*?(?=\s)"

 #to fix the above problem you can use sed to remove spaces from the file path only
 ## as a side note unfortunately sed has limited regex capibility and it must be written out in long hand.
 NewStrFP=$(echo $StrFP | sed 's:\(/[a-z]*\)\( \)\([a-z]*/\):\1\3:g')
 grep -oP "$regex" <<< $NewStrFP

正则表达式的整体解决方案:

即使文件名中有多个“.”,此函数也可以为您提供带或不带 linux 文件路径扩展名的文件名。 它还可以处理文件路径中的空格以及文件路径是否嵌入或包装在字符串中。

#you may notice that the sed replace has gotten really crazy looking
#I just added all of the allowed characters in a linux file path
function Get-FileName()
    local FileString="$1"
    local NoExtension="$2"
    local FileString=$(echo $FileString | sed 's:\(/[a-zA-Z0-9\<\>\|\\\:\)\(\&\;\,\?\*]*\)\( \)\([a-zA-Z0-9\<\>\|\\\:\)\(\&\;\,\?\*]*/\):\1\3:g')

    local regex="(?<=/)[^/]*?(?=\s)"

    local FileName=$(echo $FileString | grep -oP "$regex")

    if [[ "$NoExtension" != "" ]]; then
        sed 's:\.[^\.]*$::g' <<< $FileName
    else
        echo "$FileName"
    fi


## call the function with extension
Get-FileName "my string is awesome file: /hel lo/world/my/file test/path/hello_my_filename.log sweet path bro."

##call function without extension
Get-FileName "my string is awesome file: /hel lo/world/my/file test/path/hello_my_filename.log sweet path bro." "1"

如果你必须弄乱一个 windows 路径,你可以从这个开始:

 [^\\]*$       

【讨论】:

【参考方案6】:
$ file=$$(basename $file_path)%.*

【讨论】:

这会在 bash v4.4.7 中返回“错误替换”。我认为 Fırat KÜÇÜK 的 sed 解决方案更好,即 $(basename $the_file_path) | sed "s/\..*//" 我的意思是echo $(basename $the_file_path) | sed "s/\..*//" 文件名中的空格和点是否有效?如果没有,怎么办? 删除文件名中的空格和点。 对我来说,这也会在 Bash 4.2.46 中产生“错误替换”错误。但如果我替换运算符的顺序,即 file=$(basename $file_path%.*)

以上是关于仅从 Bash 脚本中的路径获取文件名 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

读取bash脚本中的输入,该脚本是路径中带有空格的目录[重复]

在 bash 脚本中获取祖父目录 - 为路径中的目录重命名文件

确定执行 BASH 脚本的路径 [重复]

如何将文件路径作为 bash 脚本中的参数传递给函数? [复制]

被另一个bash脚本调用后获取文件的当前目录[重复]

通过bash脚本获取Android Studio项目的构建文件夹路径