给定someletters_12345_moreleters.ext
形式的文件名,我想提取5位数字并将它们放入变量中。
因此,为了强调这一点,我有一个带有x个字符的文件名,然后是一个五位数序列,两边都是一个下划线,然后是另一组x个字符。我想取5位数字并将其放入变量中。
我对可以实现的不同方式的数量非常感兴趣。
使用cut:
echo 'someletters_12345_moreleters.ext' | cut -d'_' -f 2
更通用的:
INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING
如果我们专注于以下概念: “一个(一个或几个)数字的运行”
我们可以使用几个外部工具来提取数字。 我们可以很容易地删除所有其他字符,sed或tr:
name='someletters_12345_moreleters.ext'
echo $name | sed 's/[^0-9]*//g' # 12345
echo $name | tr -c -d 0-9 # 12345
但如果$ name包含多个数字,则上述操作将失败:
如果“name = someletters_12345_moreleters_323_end.ext”,则:
echo $name | sed 's/[^0-9]*//g' # 12345323
echo $name | tr -c -d 0-9 # 12345323
我们需要使用正则表达式(正则表达式)。 要在sed和perl中仅选择第一次运行(12345而不是323):
echo $name | sed 's/[^0-9]*\([0-9]\{1,\}\).*$/\1/'
perl -e 'my $name='$name';my ($num)=$name=~/(\d+)/;print "$num\n";'
但我们也可以直接在bash(1)中做到:
regex=[^0-9]*([0-9]{1,}).*$; \
[[ $name =~ $regex ]] && echo ${BASH_REMATCH[1]}
这允许我们提取任意长度的第一轮数字 被任何其他文字/字符包围。
注意:regex=[^0-9]*([0-9]{5,5}).*$;
仅匹配5位数运行。 :-)
(1):比为每个短文本调用外部工具更快。对于在大型文件中执行sed或awk内的所有处理并不快。
这是一个前缀后缀解决方案(类似于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
这是我怎么做的:
FN=someletters_12345_moreleters.ext
[[ $FN =~ _([[:digit:]]{5})_ ]] && NUM=${BASH_REMATCH[1]}
注意:以上是正则表达式,仅限于由下划线包围的五位数的特定场景。如果需要不同的匹配,请更改正则表达式。
我喜欢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_ubstitute[0-9]+
匹配1+位数\1
链接到正则表达式输出的组n.1(组0是整个匹配,组1是在这种情况下括号内的匹配)p
标志用于_p_rinting所有逃脱\
都是为了使sed
的regexp处理工作。
鉴于test.txt是一个包含“ABCDEFGHIJKLMNOPQRSTUVWXYZ”的文件
cut -b19-20 test.txt > test1.txt # This will extract chars 19 & 20 "ST"
while read -r; do;
> x=$REPLY
> done < test1.txt
echo $x
ST
我的答案将更多地控制你想要的字符串。这是关于如何从字符串中提取12345
的代码
str="someletters_12345_moreleters.ext"
str=${str#*_}
str=${str%_more*}
echo $str
如果你想提取具有abc
之类字符或任何特殊字符(如_
或-
)的东西,这将更有效。例如:如果你的字符串是这样的,你想要的是someletters_
之后和_moreleters.ext
之前的所有内容:
str="someletters_123-45-24a&13b-1_moreleters.ext"
使用我的代码,您可以提到您想要的内容。说明:
#*
它将删除前面的字符串,包括匹配的键。这里我们提到的关键是_
%
它将删除包括匹配键的以下字符串。我们提到的关键是'_more *'
自己做一些实验,你会发现这很有趣。
类似于php中的substr('abcdefg',2-1,3):
echo 'abcdefg'|tail -c +2|head -c 3
好的,这里使用空字符串进行纯参数替换。警告是我已经将someletters和moreletters定义为唯一的字符。如果它们是字母数字,则不会按原样运行。
filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345
还有bash builtin'expr'命令:
INPUT="someletters_12345_moreleters.ext"
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `
echo $SUBSTRING
有点晚了,但我遇到了这个问题,发现了以下内容:
host:/tmp$ asd=someletters_12345_moreleters.ext
host:/tmp$ echo `expr $asd : '.*_\(.*\)_'`
12345
host:/tmp$
我用它在一个没有%N日期的嵌入式系统上获得毫秒分辨率:
set `grep "now at" /proc/timer_list`
nano=$3
fraction=`expr $nano : '.*\(...\)......'`
$debug nano is $nano, fraction is $fraction
如果x是常量,则以下参数扩展执行子字符串提取:
b=${a:12:5}
其中12是偏移量(从零开始),5是长度
如果数字周围的下划线是输入中的唯一下划线,则可以分两步删除前缀和后缀(分别):
tmp=${a#*_} # remove prefix ending in "_"
b=${tmp%_*} # remove suffix starting with "_"
如果有其他下划线,无论如何它可能是可行的,尽管更棘手。如果有人知道如何在单个表达式中执行两个扩展,我也想知道。
所提出的两种解决方案都是纯粹的bash,没有涉及过程产生,因此非常快。
一个bash解决方案:
IFS="_" read -r x digs x <<<'someletters_12345_moreleters.ext'
这将破坏一个名为x
的变量。 var x
可以改为var _
。
input='someletters_12345_moreleters.ext'
IFS="_" read -r _ digs _ <<<"$input"
通用解决方案,其中数字可以是文件名中的任何位置,使用第一个这样的序列:
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:]]')
只是尝试使用cut -c startIndx-stopIndx
如果有人想要更严格的信息,你也可以像这样在man bash中搜索它
$ man bash [press return key]
/substring [press return key]
[press "n" key]
[press "n" key]
[press "n" key]
[press "n" key]
结果:
${parameter:offset} ${parameter:offset:length} Substring Expansion. Expands to up to length characters of parameter starting at the character specified by offset. If length is omitted, expands to the substring of parameter start‐ ing at the character specified by offset. length and offset are arithmetic expressions (see ARITHMETIC EVALUATION below). If offset evaluates to a number less than zero, the value is used as an offset from the end of the value of parameter. Arithmetic expressions starting with a - must be separated by whitespace from the preceding : to be distinguished from the Use Default Values expansion. If length evaluates to a number less than zero, and parameter is not @ and not an indexed or associative array, it is interpreted as an offset from the end of the value of parameter rather than a number of characters, and the expan‐ sion is the characters between the two offsets. If parameter is @, the result is length positional parameters beginning at off‐ set. If parameter is an indexed array name subscripted by @ or *, the result is the length members of the array beginning with ${parameter[offset]}. A negative offset is taken relative to one greater than the maximum index of the specified array. Sub‐ string expansion applied to an associative array produces unde‐ fined results. Note that a negative offset must be separated from the colon by at least one space to avoid being confused with the :- expansion. Substring indexing is zero-based unless the positional parameters are used, in which case the indexing starts at 1 by default. If offset is 0, and the positional parameters are used, $0 is prefixed to the list.
基于jor的答案(这对我不起作用):
substring=$(expr "$filename" : '.*_\([^_]*\)_.*')
我很惊讶这个纯粹的bash解决方案没有出现:
a="someletters_12345_moreleters.ext"
IFS="_"
set $a
echo $2
# prints 12345
您可能想要将IFS重置为之前的值,或之后的unset IFS
!
遵循要求
我有一个带有x个字符的文件名,然后是一个五位数序列,两边都是一个下划线,然后是另一组x个字符。我想取5位数字并将其放入变量中。
我找到了一些可能有用的grep
方法:
$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]+"
12345
或更好
$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]{5}"
12345
然后用-Po
语法:
$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d+'
12345
或者如果你想让它恰好适合5个字符:
$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d{5}'
12345
最后,为了使它存储在变量中,只需要使用var=$(command)
语法。
没有任何子流程,您可以:
shopt -s extglob
front=${input%%_+([a-zA-Z]).*}
digits=${front##+([a-zA-Z])_}
一个非常小的变体也适用于ksh93。