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

下面的代码有什么问题?

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

像这样,我得到了字符串字面量$filename | cut -f1 -d'.',但如果我删除引号,我什么也得不到。与此同时,打字

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

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

388211 次浏览

当你想在script/command中执行命令时,你应该使用命令替换语法$(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

你也可以使用参数展开:

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

只是要注意,如果没有文件扩展名,它将进一步向后寻找点,例如。

  • 如果文件名仅以点开始(例如.bashrc),它将删除整个文件名。
  • 如果路径中只有一个点(例如path.to/myfile./myfile),那么它将在路径内部进行修整。

如果你知道扩展名,你可以使用basename

$ basename /home/jsmith/base.wiki .wiki
base

如果你的文件名包含一个点(除了扩展名),那么使用这个:

echo $filename | rev | cut -f 2- -d '.' | rev
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

你想用哪个就用哪个。这里我假设最后的.(点)后面跟着文本是扩展。

你的代码有两个问题:

  1. 您使用' (tick)而不是'(反tick)来包围生成想要存储在变量中的字符串的命令。
  2. 您没有将变量“$filename”“echo”到管道中的“cut”命令中。

我会把你的代码改为"name= ' echo $filename | cut -f 1 -d '。' ' ",如下所示(再次注意,后面的勾号围绕着name变量定义):

$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$>
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

输出:

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

输出:

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

注意,只有最后一个扩展被删除。

正如Hawker65在chepner answer的评论中指出的那样,投票最多的解决方案既不关心多个扩展名(如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)

我的建议是使用basename 它在Ubuntu中是默认的,视觉上简单的代码,可以处理大多数情况

下面是一些处理空格和多点/子扩展的子案例:

pathfile="../space fld/space -file.tar.gz"
echo ${pathfile//+(*\/|.*)}

它通常摆脱第一个.的扩展,但在我们的..路径失败

echo **"$(basename "${pathfile%.*}")"**
space -file.tar     # I believe we needed exatly that

这里有一个重要的提示:

我用双引号内双引号来处理空格。 单报价将不通过由于短信$。 Bash是不寻常的,由于扩展,读取“第二个”和“第一个”引号。< / p >

然而,你仍然需要考虑.hidden_files

hidden="~/.bashrc"
echo "$(basename "${hidden%.*}")"  # will produce "~" !!!

不是预期的“”结果。要实现它,请使用$HOME/home/user_path/
因为bash是“不寻常的”,不要展开“~”(搜索bash bash)

hidden2="$HOME/.bashrc" ;  echo '$(basename "${pathfile%.*}")'

之前提供的答案在包含点的路径上有问题。一些例子:

/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

但是,此方法在“。files”中将失败;无分机:

$ echo "/a.b.c/.profile" | sed -e 's/\.[^./]*$//'
/a.b.c/

为了涵盖这些情况,你可以使用:

$ echo "/a.b.c/.profile" | sed -re 's/(^.*[^/])\.[^./]*$/\1/'
/a.b.c/.profile

仅使用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

这个包含了所有的可能性!(点在路径上与否;延长或不延长):

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"

enter image description here

假设你的文件有。new扩展名

ls -1 | awk '{ print "mv "$1" `basename "$1" .new`"}' | sh

由于张贴后不显示特别报价,请参阅图片。

在Zsh:

fullname=bridge.zip
echo ${fullname:r}

它简单,干净,可以被链接以删除多个扩展:

fullname=bridge.tar.gz
echo ${fullname:r:r}

并且它可以与其他类似的修饰符