提取基本(减去后缀)文件名的最后 3 个字符的最短方法

Jas*_*n C 12 shell-script filenames

我正在尝试将 sh 脚本中的变量设置为文件基本名称的最后 3 个字符(基本名称是指没有路径后缀)。我已经成功地做到了这一点,但纯粹出于好奇,我想知道是否有一个更短的单一命令可以使用。最初我有一个带有 的单线awk,但它相当长。目前我有这个两行脚本(假设完整的文件名在$1):

filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`
Run Code Online (Sandbox Code Playgroud)

因此,例如,“/path/to/somefile.txt”结束了“ILE”$lastpart

我可以以某种方式将basename后缀合并到一个命令中,有没有办法在tail不使用管道的情况下将其发送到(或我可以使用的其他东西)?后缀未知,因此我无法将其作为basename.

主要目标实际上并不是尽可能短,而是尽可能一目了然。所有这一切的实际背景是Superuser 上的这个问题,我试图在其中提出一个相当简单的答案。

mik*_*erv 13

var=123456
echo "${var#"${var%???}"}"

###OUTPUT###

456
Run Code Online (Sandbox Code Playgroud)

首先删除最后三个字符 from$var然后从删除$var结果中删除 - 返回$var. 以下是一些更具体的示例,旨在展示您如何做这样的事情:

touch file.txt
path=${PWD}/file.txt
echo "$path"

/tmp/file.txt

base=${path##*/}
exten=${base#"${base%???}"}
base=${base%."$exten"}
{ 
    echo "$base" 
    echo "$exten" 
    echo "${base}.${exten}" 
    echo "$path"
}

file
txt
file.txt
/tmp/file.txt
Run Code Online (Sandbox Code Playgroud)

您不必通过如此多的命令将其全部展开。你可以压缩这个:

{
    base=${path##*/} exten= 
    printf %s\\n "${base%.*}" "${exten:=${base#"${base%???}"}}" "$base" "$path"
    echo "$exten"
}

file 
txt 
file.txt 
/tmp/file.txt
txt
Run Code Online (Sandbox Code Playgroud)

结合$IFSset婷壳参数也可以解析和通过外壳变量钻的一个非常有效的手段:

(IFS=. ; set -f; set -- ${path##*/}; printf %s "${1#"${1%???}"}")
Run Code Online (Sandbox Code Playgroud)

这只会让你在最后一个/in之后的第一个句点之前的三个字符$path。如果您只想检索紧接在 last .in之前的前三个字符$path (例如,如果.文件名中可能有多个字符

(IFS=.; set -f; set -- ${path##*/}; ${3+shift $(($#-2))}; printf %s "${1#"${1%???}"}")
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,您都可以执行以下操作:

newvar=$(IFS...)
Run Code Online (Sandbox Code Playgroud)

和...

(IFS...;printf %s "$2")
Run Code Online (Sandbox Code Playgroud)

...将打印以下内容 .

如果您不介意使用外部程序,您可以执行以下操作:

printf %s "${path##*/}" | sed 's/.*\(...\)\..*/\1/'
Run Code Online (Sandbox Code Playgroud)

如果\n文件名中可能出现ewline 字符(不适用于本机 shell 解决方案——无论如何它们都会处理)

printf %s "${path##*/}" | sed 'H;$!d;g;s/.*\(...\)\..*/\1/'
Run Code Online (Sandbox Code Playgroud)


Sté*_*las 6

这是一个典型的工作expr

$ file=/path/to/abcdef.txt
$ expr "/$file" : '.*\([^/.]\{3\}\)\.[^/.]*$'
def
Run Code Online (Sandbox Code Playgroud)

如果您知道您的文件名具有预期的格式(包含一个且仅一个点且该点前至少有 3 个字符),则可以简化为:

expr "/$file" : '.*\(.\{3\}\)\.'
Run Code Online (Sandbox Code Playgroud)

请注意,如果不匹配,退出状态将为非零,而且如果匹配的部分是解析为 0 的数字。(如 fora000.txta-00.txt

zsh

file=/path/to/abcdef.txt
lastpart=${${file:t:r}[-3,-1]}
Run Code Online (Sandbox Code Playgroud)

:t用于尾部(基本名称),:r用于其余部分(删除扩展名))。

  • 好的。`expr` 是另一个我需要熟悉的。我*真的*喜欢一般的`zsh`解决方案(我昨天也在阅读它对`${}`左侧嵌套替换的支持,并希望`sh`也有同样的功能),它只是一个无赖默认情况下它并不总是存在。 (2认同)
  • @JasonC - 信息最重要。尽可能地利用它——这就是系统的全部意义所在。如果代表买了食物,我可能会不高兴,但更多时候*(比从来没有)* 信息带回家的培根 (2认同)