在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"