给定表单中的文件名someletters_12345_moreleters.ext
,我想提取 5 位数字并将它们放入变量中。
因此,为了强调这一点,我有一个包含 x 个字符的文件名,然后是一个五位数字序列,两边各有一个下划线,然后是另一组 x 个字符。我想取 5 位数字并将其放入变量中。
我对可以实现这一目标的不同方式的数量非常感兴趣。
您可以使用参数扩展来执行此操作。
如果a是常量,则以下参数扩展执行子字符串提取:
b=${a:12:5}
其中12是偏移量(从零开始),5是长度
如果数字周围的下划线是输入中唯一的下划线,则可以分两步去除前缀和后缀(分别):
tmp=${a#*_} # remove prefix ending in "_"
b=${tmp%_*} # remove suffix starting with "_"
如果还有其他下划线,无论如何它可能是可行的,尽管更棘手。如果有人知道如何在一个表达式中执行两个扩展,我也想知道。
提出的两种解决方案都是纯 bash,不涉及进程生成,因此速度非常快。
使用剪切:
echo 'someletters_12345_moreleters.ext' | cut -d'_' -f 2
更通用:
INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING
通用解决方案,其中数字可以在文件名中的任何位置,使用第一个这样的序列:
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
这是我的做法:
FN=someletters_12345_moreleters.ext
[[ ${FN} =~ _([[:digit:]]{5})_ ]] && NUM=${BASH_REMATCH[1]}
解释:
特定于 Bash 的:
[[ ]]
表示条件表达式=~
表示条件是正则表达式&&
如果先前的命令成功,则链接命令正则表达式 (RE):_([[:digit:]]{5})_
_
是用于为被匹配的字符串划分/锚定匹配边界的文字()
创建捕获组[[:digit:]]
是一个角色类,我认为它不言自明{5}
表示前一个字符、类(如本例中)或组中的五个必须匹配在英语中,你可以认为它的行为是这样的:FN
字符串逐个字符地迭代,直到我们看到一个_
捕获组被打开并且我们尝试匹配五个数字。如果此时匹配成功,则捕获组保存遍历的五个数字。如果下一个字符是_
,则条件成功,捕获组在 中可用BASH_REMATCH
,并且NUM=
可以执行下一条语句。如果匹配的任何部分失败,保存的详细信息将被处理掉,并在_
. 例如,如果FN
where _1 _12 _123 _1234 _12345_
,在找到匹配项之前会有四个错误的开始。
如果有人想要更严格的信息,你也可以像这样在 man bash 中搜索
$ man bash [press return key]
/substring [press return key]
[press "n" key]
[press "n" key]
[press "n" key]
[press "n" key]
结果:
${参数:偏移量} ${参数:偏移量:长度} 子串扩展。扩展到最多长度字符 参数从 offset 指定的字符开始。如果 length 被省略,展开为参数 start- 的子字符串 在由 offset 指定的字符处。长度和偏移量是 算术表达式(见下面的算术评估)。如果 offset 计算为小于零的数字,使用该值 作为参数值末尾的偏移量。算术 以 - 开头的表达式必须用空格分隔 与前面的:要区别于使用默认值 价值观扩张。如果长度计算结果小于 零,并且参数不是@,也不是索引或关联 数组,它被解释为从值末尾的偏移量 参数而不是字符数,以及扩展 sion 是两个偏移量之间的字符。如果参数是 @,结果是从 off 开始的长度位置参数 放。如果参数是由@ 或下标的索引数组名称 *,结果是以数组开头的长度成员 ${参数[偏移]}。相对于 比指定数组的最大索引大一。子 应用于关联数组的字符串扩展会产生不正确的 罚款结果。请注意,必须将负偏移量分开 与冒号相距至少一个空格以避免混淆 使用 :- 扩展。子字符串索引是从零开始的,除非 使用位置参数,在这种情况下,索引 默认从 1 开始。如果偏移量为 0,则位置 使用参数,$0 是列表的前缀。
我很惊讶这个纯 bash 解决方案没有出现:
a="someletters_12345_moreleters.ext"
IFS="_"
set $a
echo $2
# prints 12345
您可能希望将 IFS 重置为之前或unset IFS
之后的值!
基于 jor 的回答(这对我不起作用):
substring=$(expr "$filename" : '.*_\([^_]*\)_.*')
如果我们关注以下概念:
“一系列(一个或几个)数字”
我们可以使用几个外部工具来提取数字。
我们可以很容易地擦除所有其他字符,无论是 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
我们需要使用正则表达式(regex)。
要在 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 中对大文件进行所有处理快。
遵循要求
我有一个包含 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 中使用。
这是一个前缀-后缀解决方案(类似于 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
我的回答将更好地控制你想要从你的字符串中得到什么。这是有关如何12345
从字符串中提取的代码
str="someletters_12345_moreleters.ext"
str=${str#*_}
str=${str%_more*}
echo $str
abc
如果您想提取具有任何字符或任何特殊字符(如_
or )的内容,这将更有效-
。例如:如果您的字符串是这样的,并且您想要之后someletters_
和之前的所有内容_moreleters.ext
:
str="someletters_123-45-24a&13b-1_moreleters.ext"
使用我的代码,您可以提及您到底想要什么。解释:
#*
它将删除前面的字符串,包括匹配的键。这里我们提到的键是_
%
它将删除以下字符串,包括匹配的键。这里我们提到的关键是'_more*'
自己做一些实验,你会发现这很有趣。
我喜欢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.
更多关于这一点,如果你对正则表达式不太自信:
s
是为_s_substitute[0-9]+
匹配 1+ 位数\1
链接到正则表达式输出的组 n.1(组 0 是整个匹配,组 1 是在这种情况下括号内的匹配)p
标志用于 _p_rinting所有的转义\
都是为了使sed
' 的正则表达式处理工作。
给定 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
shell cut - 从字符串中打印特定范围的字符或给定部分
#method1) 使用 bash
str=2020-08-08T07:40:00.000Z
echo ${str:11:8}
#method2) 使用剪切
str=2020-08-08T07:40:00.000Z
cut -c12-19 <<< $str
#method3) 使用 awk 时
str=2020-08-08T07:40:00.000Z
awk '{time=gensub(/.{11}(.{8}).*/,"\\1","g",$1); print time}' <<< $str
类似于 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 'expr' 命令:
INPUT="someletters_12345_moreleters.ext"
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `
echo $SUBSTRING
一个bash解决方案:
IFS="_" read -r x digs x <<<'someletters_12345_moreleters.ext'
这将破坏一个名为x
. varx
可以更改为 var _
。
input='someletters_12345_moreleters.ext'
IFS="_" read -r _ digs _ <<<"$input"
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
可能这可以帮助您获得所需的输出
代码 :
your_number=$(echo "someletters_12345_moreleters.ext" | grep -E -o '[0-9]{5}')
echo $your_number
输出 :
12345
有点晚了,但我刚刚遇到了这个问题,发现了以下内容:
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
这是一个 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