Извлечь подстроку в Bash

задано имя файла в виде someletters_12345_moreleters.ext, Я хочу извлечь 5 цифр и поместить их в переменную.

Итак, чтобы подчеркнуть этот момент, у меня есть имя файла с X количеством символов, а затем пятизначная последовательность, окруженная одним подчеркиванием с обеих сторон, а затем другой набор x количество символов. Я хочу взять 5-значное число и поместить его в переменную.

меня очень интересует число различных способов, которыми это может быть достигнуто.

20 ответов


использовать вырезать:

echo 'someletters_12345_moreleters.ext' | cut -d'_' -f 2

более общий:

INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING

Если x является константой, следующее расширение параметра выполняет извлечение подстроки:

b=${a:12:5}

здесь 12 является смещением (на основе нуля) и 5 длина

Если подчеркивания вокруг цифр являются единственными на входе, Вы можете удалить префикс и суффикс (соответственно) в два шага:

tmp=${a#*_}   # remove prefix ending in "_"
b=${tmp%_*}   # remove suffix starting with "_"

Если есть другие подчеркивания, это, вероятно, возможно в любом случае, хотя и более сложно. Если кто-нибудь знает, как выполнять оба расширения в одном выражении, я тоже хотел бы знать.

оба представленных решения-чистый Баш, без процесса нереста, следовательно, очень быстро.


общее решение, где число может быть в любом месте имени файла, используя первую из таких последовательностей:

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

другое решение для извлечения точно части переменной:

number=${filename:offset:length}

если ваше имя файла всегда имеет формат stuff_digits_... вы можете использовать на awk:

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

еще одно решение для удаления всего, кроме цифр, используйте

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,  is prefixed to the list.

основываясь на ответе Джора (который не работает для меня):

substring=$(expr "$filename" : '.*_\([^_]*\)_.*')

Я удивлен, что это чистое решение bash не пришло:

a="someletters_12345_moreleters.ext"
IFS="_"
set $a
echo 
# 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.


Если мы сосредоточимся на понятии:
"Пробег (одной или нескольких) цифр"

мы могли бы использовать несколько внешних инструментов для извлечения номера.
Мы могли бы легко стереть все остальные символы, либо sed, либо tr:

name='someletters_12345_moreleters.ext'

echo $name | sed 's/[^0-9]*//g'    # 12345
echo $name | tr -c -d 0-9          # 12345

но если $name содержит несколько пробегов чисел, вышеприведенное не удастся:

If " name=someletters_12345_moreleters_323_end.доб", затем:

echo $name | sed 's/[^0-9]*//g'    # 12345323
echo $name | tr -c -d 0-9          # 12345323

нам нужно использовать регулярные выражения (регулярное выражение.)
Чтобы выбрать только первый запуск (12345 не 323) в sed и perl:

echo $name | sed 's/[^0-9]*\([0-9]\{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]\+\).*//p" -n )
> echo $digits
12345

немного более общий вариант будет не предположить, что у вас есть подчеркивания _ маркировка начала последовательности цифр, следовательно, например, удаление всех не-чисел, которые вы получаете перед вашей последовательностью:s/[^0-9]\+\([0-9]\+\).*//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  through  to refer to the corresponding matching sub-expressions in the regexp.

подробнее об этом, если вы не слишком уверены в регулярных выражениях:

  • s для _s_ubstitute
  • [0-9]+ соответствует 1 + цифрам
  • ссылки на группу n.1 вывода регулярного выражения (группа 0-это все совпадение, группа 1-совпадение в скобках в этом случае)
  • p флаг предназначен для _p_rinting

все убегало \ сделать sedregexp обработки работы.


данного теста.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

подобно substr ('abcdefg', 2-1, 3) в php:

echo 'abcdefg'|tail -c +2|head -c 3

мой ответ будет иметь больше контроля над тем, что вы хотите из своей строки. Вот код о том, как вы можете извлечь 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*'

у некоторых опытах себе, и вы найдете это интересным.


есть также команда bash builtin 'expr':

INPUT="someletters_12345_moreleters.ext"  
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `  
echo $SUBSTRING

Ok, здесь идет чистая подстановка параметров с пустой строкой. Оговорка заключается в том, что я определил someletters и moreletters как только символы. Если они буквенно-цифровые, это не будет работать так, как есть.

filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
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=
fraction=`expr $nano : '.*\(...\)......'`
$debug nano is $nano, fraction is $fraction

решение bash:

IFS="_" read -r x digs x <<<'someletters_12345_moreleters.ext'

это ударит переменную под названием x. ВАР x можно изменить на var _.

input='someletters_12345_moreleters.ext'
IFS="_" read -r _ digs _ <<<"$input"