在Bash中提取子string
给定一个文件名的formssomeletters_12345_moreleters.ext
,我想提取5位数字,并把它们放入一个variables。
所以为了强调这一点,我有一个文件名,其中包含x个字符,然后是一个五位数字序列,两边都是单个下划线,然后是另外一组x个字符。 我想采取5位数字,并将其放入一个variables。
我对这个可以完成的不同方式的数量非常感兴趣。
使用剪切 :
echo 'someletters_12345_moreleters.ext' | cut -d'_' -f 2
更一般的:
INPUT='someletters_12345_moreleters.ext' SUBSTRING=$(echo $INPUT| cut -d'_' -f 2) echo $SUBSTRING
如果x是常量,则以下参数扩展将执行子string提取:
b=${a:12:5}
其中12是偏移量(基于零), 5是长度
如果数字的下划线是input中唯一的下划线,则可以分两步去除前缀和后缀:
tmp=${a#*_} # remove prefix ending in "_" b=${tmp%_*} # remove suffix starting with "_"
如果还有其他的下划线,反正可能是可行的,尽pipe比较棘手。 如果有人知道如何在单个expression式中执行两个扩展,我也想知道。
提出的两个解决scheme都是纯粹的bash,不涉及进程产卵,因此非常快。
通用的解决scheme,其中的数字可以在文件名中的任何地方,使用第一个这样的序列:
number=$(echo $filename | egrep -o '[[:digit:]]{5}' | head -n1)
另一个解决scheme,提取一个variables的一部分:
number=${filename:offset:length}
如果你的文件名总是具有格式stuff_digits_...
你可以使用awk:
number=$(echo $filename | awk -F _ '{ print $2 }')
还有另一种解决办法,除了数字,使用
number=$(echo $filename | tr -cd '[[:digit:]]')
只是尝试使用cut -c startIndx-stopIndx
如果有人想要更加严格的信息,你也可以像这样在人群中search
$ man bash [press return key] /substring [press return key] [press "n" key] [press "n" key] [press "n" key] [press "n" key]
结果:
$ {参数:偏移} $ {参数:偏移量:长度} 子串扩展。 扩展到长度的字符 参数从offset指定的字符开始。 如果 长度被省略,扩展到参数start- 在由offset指定的字符处。 长度和偏移量都是 算术expression式(见下面的算术评估)。 如果 偏移量计算为小于零的数字,则使用该值 作为参数值的结尾的偏移量。 算术 以 - 开头的expression式必须用空格分隔 从前面:与“使用默认值”区分开来 价值观扩张。 如果长度评估数小于 零,参数不是@而不是索引或关联 数组,它被解释为从值的末尾的偏移量 的参数而不是一些字符,而expan- sion是两个偏移量之间的字符。 如果参数是 @,结果是长度位置参数从off- 组。 如果参数是由@或者下标的索引数组名称 *,结果是以数组开头的长度成员 $ {参数[偏移]}。 相对于一个负的偏移量 一个大于指定数组的最大索引。 子 应用于关联数组的string扩展会产生unde- 罚款结果。 请注意,负偏移量必须分开 从结肠至less有一个空间,以避免混淆 与: - 扩大。 子string索引是基于零的,除非 使用位置参数,在这种情况下索引 默认从1开始。 如果偏移量是0,则位置 参数被使用,$ 0被加到列表的前面。
我很惊讶这个纯粹的bash解决scheme没有出现:
a="someletters_12345_moreleters.ext" IFS="_" set $a echo $2 # prints 12345
您可能想要将IFS重置为之前的值,或者之后未unset IFS
!
build立在乔尔的答案(这不适合我):
substring=$(expr "$filename" : '.*_\([^_]*\)_.*')
遵循要求
我有一个带有x个字符的文件名,然后是一个五位数字的序列,在两边都有一个下划线,然后是另外一组x个字符。 我想采取5位数字,并将其放入一个variables。
我发现了一些可能有用的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
最后,为了将它存储在variables中,只需要使用var=$(command)
语法。
没有任何子stream程,您可以:
shopt -s extglob front=${input%%_+([a-zA-Z]).*} digits=${front##+([a-zA-Z])_}
这个的一个非常小的变种也将在ksh93中工作。
如果我们把重点放在:
“(一个或几个)数字的运行”
我们可以使用几个外部工具来提取数字。
我们可以很容易地删除所有其他字符,无论是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
我们需要使用正则expression式(正则expression式)。
要仅selectsed和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中处理大文件的所有处理速度快。
以下是我该怎么做:
FN=someletters_12345_moreleters.ext [[ $FN =~ _([[:digit:]]{5})_ ]] && NUM=${BASH_REMATCH[1]}
注意:上面是一个正则expression式,并且被限制在由下划线包围的五位数的特定场景中。 如果您需要不同的匹配,请更改正则expression式。
这里有一个前缀后缀解决scheme(类似于JB和Darron给出的解决scheme),它与第一个数字块匹配,不依赖于周围的下划线:
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
类似于substr('abcdefg',2-1,3)在php:
echo 'abcdefg'|tail -c +2|head -c 3
我的答案将有更多的控制你想要什么你的string。 这里是如何从string中提取12345
的代码
str="someletters_12345_moreleters.ext" str=${str#*_} str=${str%_more*} echo $str
如果你想提取任何字符如abc
或任何特殊字符(如_
或-
这将更有效。 例如:如果你的string是这样的,你希望在someletters_
之后和_moreleters.ext
之前的所有_moreleters.ext
:
str="someletters_123-45-24a&13b-1_moreleters.ext"
用我的代码,你可以提到你想要什么。 说明:
#*
将删除包含匹配键的前面的string。 这里我们提到的关键是_
%
它将删除包含匹配键的以下string。 这里我们提到的关键是'_more *'
你自己做一些实验,你会发现这个有趣的。
我喜欢sed
处理正则expression式的能力:
> var="someletters_12345_moreletters.ext" > digits=$( echo $var | sed "s/.*_\([0-9]\+\).*/\1/p" -n ) > echo $digits 12345
稍微更一般的select是不要假设你有一个下划线_
标记你的数字序列的开始,因此例如剥离你的序列之前得到的所有非数字: 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.
更多关于这个,如果你对正则expression式不太自信:
-
s
代表_s_ubstitute -
[0-9]+
匹配1+个数字 -
\1
链接到正则expression式输出的组n.1(组0是整个匹配,组1是在这种情况下括号内的匹配) -
p
标志是_p_rinting
所有的逃脱都是为了使sed
的正则expression式处理工作。
还有bash内build的'expr'命令:
INPUT="someletters_12345_moreleters.ext" SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' ` echo $SUBSTRING
好的,这里用一个空string进行纯参数replace。 警告是我已经定义了一些字符和更多的字符。 如果他们是字母数字,这将不会像现在这样工作。
filename=someletters_12345_moreletters.ext substring=${filename//@(+([az])_|_+([az]).*)} echo $substring 12345
鉴于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
有点晚了,但我碰到这个问题,发现以下内容:
host:/tmp$ asd=someletters_12345_moreleters.ext host:/tmp$ echo `expr $asd : '.*_\(.*\)_'` 12345 host:/tmp$
我用它来获得一个embedded式系统的毫秒分辨率,该系统在date没有%N:
set `grep "now at" /proc/timer_list` nano=$3 fraction=`expr $nano : '.*\(...\)......'` $debug nano is $nano, fraction is $fraction
一个bash解决scheme:
IFS="_" read -rx digs x <<<'someletters_12345_moreleters.ext'
这将会摧毁一个名为x
的variables。 var x
可以更改为var _
。
input='someletters_12345_moreleters.ext' IFS="_" read -r _ digs _ <<<"$input"