在 bash 字符串切片中使用表达式的输出

在 bash 字符串切片中使用表达式的输出

我想从未知长度的路径中提取文件名的子字符串。我可以分别完成这两部分,但我想知道是否有一种方法可以在不使用临时变量的情况下将两者结合起来?

INPUT_PATH=/path/to/subfolder/file_17.txt 
# I would like to extract "17", the filname will always be 'file_XX.txt'
# The subfolder name is variable length 

TMP=$(basename ${INPUT_PATH})
FILE_NUMBER=${TMP:5:2}
echo ${FILE_NUMBER} # This works as expected

我尝试过${$(basename $INPUT_PATH):5:2},但这会产生严重的替换错误。这样做有什么技巧吗?

答案1

采用不同的方法来解决问题并提供仅使用功能的单行解决方案bash

$ cat demo.sh
#!/bin/bash

INPUT_PATH=/path/to/subfolder/file_17.txt

FILE_NUMBER=${INPUT_PATH:((${#INPUT_PATH} -6)):2}
echo ${FILE_NUMBER}
$

$./demo.sh
17

一种更简单的方法是从字符串末尾开始倒数,即

FILE_NUMBER=${INPUT_PATH: -6:2}

显然,该解决方案取决于以“##XXXX”结尾的字符串变量,其中“##”是感兴趣的两位数字,“XXXX”是字符串的最后 4 个字符。

答案2

由于您使用的是 bash,因此可以使用正则表达式匹配:

if [[ $input =~ ([[:digit:]]+)\.txt$ ]]; then
    file_num=${BASH_REMATCH[1]}
fi

答案3

最简单的方法是使用 FILE_NUMBER 而不是 TMP:

FILE_NUMBER=$(basename ${INPUT_PATH})
FILE_NUMBER=${FILE_NUMBER:5:2}

此外,使用参数扩展比调用基本名称更快:

FILE_NUMBER=${INPUT_PATH##*/}
FILE_NUMBER=${FILE_NUMBER:5:2}

您可以使用 sed 在一行上完成所有操作,但它速度较慢且可读性较差:

FILE_NUMBER=$(sed 's|.*/||;s/.....\(..\).*/\1/' <<<"$INPUT_PATH")

答案4

对于那些使用zsh而不是bash

  • 第 6 至 7 个字符尾巴basename路径的(由 )返回的部分:

    num=${${a_path:t}[6,7]}
    

    $var:tcsh得到tail)

    您还可以使用最近添加的num=${"$(basename -- "$a_path")":5:2}该运算符来实现兼容性。命令替换必须加引号,这样它就不会生成数组,从而选择一系列字符而不是一系列数组项。然而,使用命令替换和执行最终会比使用内置运算符效率更低、可靠性更低。ksh93 ${var:offset:length}zsh:5:2basenamezsh:t

  • 第一个数字序列尾巴:

    num=${(MS)${a_path:t}##<->}
    

    ${var##pattern}pattern是 ksh 运算符,它删除与from匹配的最长前导字符串$var。使用该M标志,M附加的部分将被返回而不是被剥离,并S查找Substring,而不仅仅是在开头。并<->匹配任何数字序列(<x-y>具有未指定的边界)。

  • _a后面的数字序列尾巴:

    num=${${a_path:t}/(#m)*_(<->)*/$match[1]}
    

    (需要extendedglob;注意如果没有_digitsin ,它会返回完整的尾部$a_path)。

    或者:

    [[ $a_path:t =~ '_([[:digit:]]+)' ]] && num=$match[1]
    

相关内容