如何在 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 脚本中删除文件名的扩展名?的主要内容,如果未能解决你的问题,请参考以下文章

用for循环编写删除文件的shell脚本

如何从shell脚本中的变量中删除回车

如何从项目中删除所有 .pyc 文件?

如何将文件删除到 shell:从 vbs 启动?

请教一个批量删除文本文件中多余回车或提取文本中前两行数据的shell脚本,请高手指点,万分感谢

Win7如何批量删除.svn文件