给定someletters_12345_moreleters形式的文件名。ext,我想提取5位数字,并将它们放入一个变量。

为了强调这一点,我有一个x个字符的文件名,然后是一个5位数字序列,两边都有一个下划线,然后是另一组x个字符。我想把这个5位数代入一个变量。

我对实现这一目标的多种不同方式非常感兴趣。


当前回答

下面是一个前缀后缀解决方案(类似于JB和Darron给出的解决方案),它匹配第一个数字块,并且不依赖于周围的下划线:

str='someletters_12345_morele34ters.ext'
s1="${str#"${str%%[[:digit:]]*}"}"   # strip off non-digit prefix from str
s2="${s1%%[^[:digit:]]*}"            # strip off non-digit suffix from s1
echo "$s2"                           # 12345

其他回答

下面是一个前缀后缀解决方案(类似于JB和Darron给出的解决方案),它匹配第一个数字块,并且不依赖于周围的下划线:

str='someletters_12345_morele34ters.ext'
s1="${str#"${str%%[[:digit:]]*}"}"   # strip off non-digit prefix from str
s2="${s1%%[^[:digit:]]*}"            # strip off non-digit suffix from s1
echo "$s2"                           # 12345

Inklusive end,类似于JS和Java实现。删除+1如果你不想这样做。

function substring() {
    local str="$1" start="${2}" end="${3}"
    
    if [[ "$start" == "" ]]; then start="0"; fi
    if [[ "$end"   == "" ]]; then end="${#str}"; fi
    
    local length="((${end}-${start}+1))"
    
    echo "${str:${start}:${length}}"
} 

例子:

    substring 01234 0
    01234
    substring 012345 0
    012345
    substring 012345 0 0
    0
    substring 012345 1 1
    1
    substring 012345 1 2
    12
    substring 012345 0 1
    01
    substring 012345 0 2
    012
    substring 012345 0 3
    0123
    substring 012345 0 4
    01234
    substring 012345 0 5
    012345

更多示例调用:

    substring 012345 0
    012345
    substring 012345 1
    12345
    substring 012345 2
    2345
    substring 012345 3
    345
    substring 012345 4
    45
    substring 012345 5
    5
    substring 012345 6
    
    substring 012345 3 5
    345
    substring 012345 3 4
    34
    substring 012345 2 4
    234
    substring 012345 1 3
    123

这是一个substring.sh文件

使用

`substring.sh $TEXT 2 3` # characters 2-3

`substring.sh $TEXT 2` # characters 2 and after 

Substring.sh遵循这一行

#echo "starting substring"
chars=$1
start=$(($2))
end=$3

i=0
o=""
if [[ -z $end ]]; then
  end=`echo "$chars " | wc -c`
else
  end=$((end))
fi
#echo "length is " $e
a=`echo $chars | sed  's/\(.\)/\1 /g'`
#echo "a is " $a
for c in $a
do
  #echo "substring" $i $e $c
  if [[ i -lt $start ]]; then
    : # DO Nothing
  elif [[ i -gt $end ]]; then
    break;
  else
    o="$o$c"
  fi
  i=$(($i+1))
done
#echo substring returning $o
echo $o

通用解决方案,其中数字可以在文件名中的任何位置,使用这样的序列中的第一个:

number=$(echo $filename | egrep -o '[[:digit:]]{5}' | head -n1)

另一个精确提取变量一部分的解决方案:

number=${filename:offset:length}

如果你的文件名总是使用stuff_digits_…你可以使用awk:

number=$(echo $filename | awk -F _ '{ print $2 }')

还有一种方法可以删除除数字以外的所有内容,使用

number=$(echo $filename | tr -cd '[[:digit:]]')

我喜欢sed处理正则表达式组的能力:

> var="someletters_12345_moreletters.ext"
> digits=$( echo "$var" | sed "s/.*_\([0-9]\+\).*/\1/p" -n )
> echo $digits
12345

一个更一般的选择是不要假设你用下划线_标记你的数字序列的开始,因此例如剥离你在你的序列之前得到的所有非数字:s/[^0-9]\+\([0-9]\+\).*/\1/p。


> man sed | grep s/regexp/replacement -A 2
s/regexp/replacement/
    Attempt to match regexp against the pattern space.  If successful, replace that portion matched with replacement.  The replacement may contain the special  character  &  to
    refer to that portion of the pattern space which matched, and the special escapes \1 through \9 to refer to the corresponding matching sub-expressions in the regexp.

更多关于这一点,以防你对regexp不太自信:

S代表_s_substitute [0-9]+匹配1+数字 \1链接到正则表达式输出的组n.1(组0是整个匹配,组1是括号内的匹配) P标志为_p_printing

所有转义\都是为了使sed的regexp处理工作。