如何在 shell 脚本中删除文件名的扩展名?
Posted
技术标签:
【中文标题】如何在 shell 脚本中删除文件名的扩展名?【英文标题】:How can I remove the extension of a filename in a shell script? 【发布时间】:2012-08-22 13:40:00 【问题描述】:下面的代码有什么问题?
name='$filename | cut -f1 -d'.''
按原样,我得到了文字字符串$filename | cut -f1 -d'.'
,但如果我删除引号,我什么也得不到。同时,输入
"test.exe" | cut -f1 -d'.'
在 shell 中给了我想要的输出,test
。我已经知道$filename
已被分配了正确的值。我想要做的是将不带扩展名的文件名分配给变量。
【问题讨论】:
basename $filename .exe
会做同样的事情。这是假设您始终知道要删除的扩展程序。
@mpe,你的意思是basename "$filename" .exe
。否则带空格的文件名将是坏消息。
【参考方案1】:
当您想在脚本/命令中执行命令时,您应该使用 command substitution 语法 $(command)
。
所以你的行将是
name=$(echo "$filename" | cut -f 1 -d '.')
代码说明:
echo
获取变量$filename
的值并发送到标准输出
然后我们获取输出并将其通过管道传送到cut
命令
cut
将使用 .作为分隔符(也称为分隔符),用于将字符串分割成段,并通过-f
我们选择要在输出中包含的段
然后$()
命令替换将获取输出并返回其值
返回值将赋值给名为name
的变量
请注意,这会给出变量的部分直到第一个句点.
:
$ filename=hello.world
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello.hello.hello
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello
$ echo "$filename" | cut -f 1 -d '.'
hello
【讨论】:
POSIX 不推荐使用反引号,首选$()
。
分叉和管道来获取几个字符是可以想象的最糟糕的解决方案。
这个答案的问题是它假设输入字符串只有一个点......下面的@chepner 有一个更好的解决方案...... name=$filename%.*
这个答案是初学者的特征,不应该传播。使用 chepner 的回答所描述的内置机制
在./ihavenoextension
上失败,就像其他答案一样。【参考方案2】:
你也可以使用参数扩展:
$ filename=foo.txt
$ echo "$filename%.*"
foo
请注意,如果没有文件扩展名,它将进一步向后查找点,例如
如果文件名仅以点开头(例如.bashrc
),它将删除整个文件名。
如果路径中只有一个点(例如path.to/myfile
或./myfile
),那么它将在路径内修剪。
【讨论】:
这里是命令的解释:gnu.org/software/bash/manual/html_node/… 这里我正要使用echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '$NF=""; print $0' | tr ' ' '.' | rev | cut -c 2- | rev
。谢谢。
这是否适用于具有多个扩展名的文件,例如image.png.gz
?
%.*
只会删除最后一个扩展名;如果要删除 所有 扩展,请使用 %%.*
。
请注意,如果没有扩展名并且路径是相对的,则会删除整个名称。例如,filename=./ihavenoextension
.【参考方案3】:
如果你知道扩展名,可以使用basename
$ basename /home/jsmith/base.wiki .wiki
base
【讨论】:
我怎样才能只删除.wiki
并以 /home/jsmith/base
结束?
更新:答案:见***.com/a/32584935/4561887。完美运行!
在我看来这应该是公认的答案
@GabrielStaples 您可以将其与dirname
结合使用,例如file=/home/jsmith/base.wiki; echo $(dirname $file)/$(basename $file .wiki)
我选择了这个。好消息是basename
为此而生,我发现了它的一项新功能。【参考方案4】:
如果您的文件名包含点(扩展名除外),请使用:
echo $filename | rev | cut -f 2- -d '.' | rev
【讨论】:
我忘记了中间的转速,但我一看到就觉得太棒了! 将-s
选项赋予cut
会更好,这样只要文件名不包含点,它就会返回一个空字符串。
在我看来这应该是公认的答案,因为它适用于带有点的路径、以点开头的隐藏文件,甚至是具有多个扩展名的文件。
这样的文件会发生什么:filename=/tmp.d/foo
?
@FedonKadifeli,这仅适用于文件名,不适用于完整路径。但是您可以先使用类似的方法获取文件名,方法是使用/
作为分隔符。【参考方案5】:
file1=/tmp/main.one.two.sh
t=$(basename "$file1") # output is main.one.two.sh
name=$(echo "$file1" | sed -e 's/\.[^.]*$//') # output is /tmp/main.one.two
name=$(echo "$t" | sed -e 's/\.[^.]*$//') # output is main.one.two
使用任何你想要的。这里我假设最后一个.
(点)后跟文本是扩展名。
【讨论】:
file1=/tmp.d/mainonetwosh
时会发生什么? sed 表达式应替换为 's/\.[^./]*$//'
【参考方案6】:
你的代码有两个问题:
-
您使用 '(记号)而不是 `(反引号)来包围生成要存储在变量中的字符串的命令。
您没有将变量“$filename”“回显”到“cut”命令的管道中。
我会将您的代码更改为“name=`echo $filename | cut -f 1 -d '。” `",如下所示(再次注意名称变量定义周围的反引号):
$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$>
【讨论】:
【参考方案7】:#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"
输出:
program
【讨论】:
这与 3 年前 Steven Penny 给出的答案有何不同? @gniourf_gniourf 赞成,因为您通过展示如何正确使用变量使其成为有用的答案。【参考方案8】:#!/bin/bash
file=/tmp/foo.bar.gz
echo $file $file%.*
输出:
/tmp/foo.bar.gz /tmp/foo.bar
请注意,仅删除了最后一个扩展名。
【讨论】:
这样的文件会发生什么:file=/tmp.d/foo
?【参考方案9】:
正如 Hawker65 在 chepner answer 的评论中指出的那样,投票最多的解决方案既不考虑多个扩展名(例如 filename.tar.gz),也不考虑路径其余部分中的点(例如 this.路径/with.dots/in.path.name)。 一个可能的解决方案是:
a=this.path/with.dots/in.path.name/filename.tar.gz
echo $(dirname $a)/$(basename $a | cut -d. -f1)
【讨论】:
这个删除“tar.gz”通过选择文件名中第一个点之前的字符而不计算路径。人们可能不想以这种方式剥离扩展。 如果您知道它以.tar.gz
结尾,您可以使用$(basename "$a" .tar.gz)
。另外,如果变量有可能包含空格或其他奇怪的字符,请确保将变量用引号括起来!【参考方案10】:
我的建议是使用basename
。
它在 Ubuntu 中是默认的,视觉上简单的代码并且可以处理大多数情况。
这里有一些处理空格和多点/子扩展的子案例:
pathfile="../space fld/space -file.tar.gz"
echo $pathfile//+(*\/|.*)
它通常会从第一个 .
删除扩展名,但在我们的 ..
路径中失败
echo **"$(basename "$pathfile%.*")"**
space -file.tar # I believe we needed exatly that
这里有一个重要提示:
我在双引号内使用双引号来处理空格。 由于向 $ 发送短信,单引号不会通过。 Bash 是不寻常的,并且由于扩展而读取“第二个“第一个”引号。
不过,你还是得想想.hidden_files
hidden="~/.bashrc"
echo "$(basename "$hidden%.*")" # will produce "~" !!!
不是预期的“”结果。要做到这一点,请使用 $HOME
或 /home/user_path/
因为 bash 又是“不寻常的”,不要扩展“~”(搜索 bash BashPitfalls)
hidden2="$HOME/.bashrc" ; echo '$(basename "$pathfile%.*")'
【讨论】:
【参考方案11】:之前提供的答案在包含点的路径方面存在问题。一些例子:
/xyz.dir/file.ext
./file.ext
/a.b.c/x.ddd.txt
我更喜欢使用|sed -e 's/\.[^./]*$//'
。例如:
$ echo "/xyz.dir/file.ext" | sed -e 's/\.[^./]*$//'
/xyz.dir/file
$ echo "./file.ext" | sed -e 's/\.[^./]*$//'
./file
$ echo "/a.b.c/x.ddd.txt" | sed -e 's/\.[^./]*$//'
/a.b.c/x.ddd
注意:如果要删除多个扩展名(如上一个示例),请使用|sed -e 's/\.[^/]*$//'
:
$ echo "/a.b.c/x.ddd.txt" | sed -e 's/\.[^/]*$//'
/a.b.c/x
但是,此方法在没有扩展名的“点文件”中会失败:
$ echo "/a.b.c/.profile" | sed -e 's/\.[^./]*$//'
/a.b.c/
要涵盖此类情况,您可以使用:
$ echo "/a.b.c/.profile" | sed -re 's/(^.*[^/])\.[^./]*$/\1/'
/a.b.c/.profile
【讨论】:
谢谢,唯一适用于./ihavenoextension
的答案!
迄今为止最好的答案!【参考方案12】:
仅使用 POSIX 的内置:
#!/usr/bin/env sh
path=this.path/with.dots/in.path.name/filename.tar.gz
# Get the basedir without external command
# by stripping out shortest trailing match of / followed by anything
dirname=$path%/*
# Get the basename without external command
# by stripping out longest leading match of anything followed by /
basename=$path##*/
# Strip uptmost trailing extension only
# by stripping out shortest trailing match of dot followed by anything
oneextless=$basename%.*; echo "$oneextless"
# Strip all extensions
# by stripping out longest trailing match of dot followed by anything
noext=$basename%%.*; echo "$noext"
# Printout demo
printf %s\\n "$path" "$dirname" "$basename" "$oneextless" "$noext"
打印演示:
this.path/with.dots/in.path.name/filename.tar.gz
this.path/with.dots/in.path.name
filename.tar.gz
filename.tar
filename
【讨论】:
【参考方案13】:这个涵盖了所有的可能性! (路径中的点与否;有扩展名或无扩展名):
tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*);echo $filename_noextension
注意事项:
它为您提供不带任何扩展名的文件名。所以$filename_noextension
变量中没有路径。
您最终会得到两个不需要的变量$tmp1
和$tmp2
。确保您没有在脚本中使用它们。
测试示例:
filename=.bashrc; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
filename=.bashrc.txt; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
filename=.bashrc.txt.tar; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
filename=~/.bashrc; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
filename=~/.bashrc.txt.tar; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
filename=bashrc; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
filename=bashrc.txt; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
filename=bashrc.txt.tar; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
filename=~/bashrc; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
filename=~/bashrc.txt.tar; echo "filename: $filename"; tmp1=$filename##*/;tmp2=$tmp1:1;filename_noextension=$(echo -n $tmp1:0:1;echo $tmp2%.*); echo "filename without extension: $filename_noextension"
【讨论】:
【参考方案14】:假设您的文件具有 .new 扩展名
ls -1 | awk ' print "mv "$1" `basename "$1" .new`"' | sh
由于发布后没有显示特殊报价,请看图片。
【讨论】:
【参考方案15】:在 Zsh 中:
fullname=bridge.zip
echo $fullname:r
它简单、干净,并且可以链接以删除多个扩展:
fullname=bridge.tar.gz
echo $fullname:r:r
并且可以和其他类似的modifiers结合使用。
【讨论】:
以上是关于如何在 shell 脚本中删除文件名的扩展名?的主要内容,如果未能解决你的问题,请参考以下文章