如何在Shell脚本中删除文件名的扩展名?


144

以下代码有什么问题?

name='$filename | cut -f1 -d'.''

照原样,我得到了文字字符串$filename | cut -f1 -d'.',但是如果删除引号,我什么也得不到。同时,输入

"test.exe" | cut -f1 -d'.'

在shell中给我想要的输出test。我已经知道$filename已经分配了正确的值。我想要做的是将没有扩展名的文件名分配给变量。


6
basename $filename .exe会做同样的事情。假设您始终知道要删除的扩展名。
mpe 2012年

6
@mpe,你的意思是basename "$filename" .exe。否则,带空格的文件名将是个坏消息。
查尔斯·达菲

Answers:


113

要在脚本/命令中执行命令时,应使用命令替换语法$(command)

所以你的线是

name=$(echo "$filename" | cut -f 1 -d '.')

代码说明:

  1. echo获取变量的值$filename并将其发送到标准输出
  2. 然后,我们获取输出并将其通过管道传递给cut命令
  3. cut会使用。作为分隔符(也称为分隔符),用于将字符串切成段,然后-f选择要在输出中包含的段
  4. 然后$()命令替换将获得输出并返回其值
  5. 返回的值将分配给名为的变量 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

谢谢。我还注意到我需要使用echo命令。名称=echo $filename | cut -f1 -d'.'
mimicocotopus 2012年

17
POSIX不推荐使用反引号,这$()是首选。
jordanm

3
分叉和管道传递几个字符是可以想象的最糟糕的解决方案。
詹斯(Jens)2015年

39
这个答案的问题是它假设输入字符串只有一个点... @下面的@chepner有一个更好的解决方案... name = $ {filename%。*}
Scott Stensland

4
这个答案是初学者的特征,不应散布。由chepner的回答说明使用内置的机制
neric

258

您还可以使用参数扩展:

$ filename=foo.txt
$ echo "${filename%.*}"
foo


1
在这里我正要使用echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev。谢谢。
user208145 '16

1
这是否适用于具有多个扩展名的文件image.png.gz
Hawker65 '18 -4-26

11
%.*只删除最后一个扩展名;如果要删除所有扩展名,请使用%%.*
chepner '18

1
这似乎比接受的答案更好。如果文件具有多个点,则仅删除最后一个扩展名,而剪切将删除第一个点之后的所有内容。
戴尔·安德森


20

如果您的文件名包含一个点(扩展名之一除外),请使用以下命令:

echo $filename | rev | cut -f 2- -d '.' | rev

1
我忘记了中间转速,但是一旦看到它,那就太好了!
至尊Pooba

最好-s给指定一个cut,这样,只要文件名不包含点,它就返回一个空字符串。
Hibou57

1
在我看来,这应该是公认的答案,因为它适用于带有点的路径,以点开头的隐藏文件,甚至具有多个扩展名的文件。
蒂姆·克里夫

20
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

使用任何您想要的。在这里,我假设最后一个.(点)后跟文本是扩展名。


6
#!/bin/bash
file=/tmp/foo.bar.gz
echo $file ${file%.*}

输出:

/tmp/foo.bar.gz /tmp/foo.bar

请注意,仅最后一个扩展名被删除。


3

我的建议是使用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%.*}")'

1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

输出:

program

这与3年前史蒂文·彭尼(Steven Penny)给出的答案有何不同?
gniourf_gniourf

1

正如Hawker65在chepner答案的评论中指出的那样,投票最多的解决方案既不考虑多个扩展名(例如filename.tar.gz),也不考虑路径其余部分中的点(例如this.path / 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”。一个人可能不想那样剥离扩展名。
Frotz

0

您的代码有两个问题:

  1. 您使用了'(勾号)而不是`(反勾号)来包围生成要存储在变量中的字符串的命令。
  2. 您没有将变量“ $ 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
$> 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.