Извлечь подстроку в Bash - PullRequest
613 голосов
/ 09 января 2009

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

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

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

Ответы [ 20 ]

947 голосов
/ 09 января 2009

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

b=${a:12:5}

, где 12 - это смещение (от нуля), а 5 - это длина

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

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

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

Оба представленных решения являются чисто bash, без порождения процессов, следовательно, очень быстрыми.

596 голосов
/ 09 января 2009

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

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

Более общий:

INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING
87 голосов
/ 09 января 2009

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

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:]]')
76 голосов
/ 22 сентября 2010

просто попробуйте использовать cut -c startIndx-stopIndx

31 голосов
/ 31 мая 2013

Если кто-то хочет получить более точную информацию, вы также можете найти ее в 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.
19 голосов
/ 09 января 2009

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

substring=$(expr "$filename" : '.*_\([^_]*\)_.*')
18 голосов
/ 03 июня 2013

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

a="someletters_12345_moreleters.ext"
IFS="_"
set $a
echo $2
# prints 12345

Вы, вероятно, хотите сбросить IFS до того значения, которое было раньше, или unset IFS впоследствии!

12 голосов
/ 26 июня 2013

В соответствии с требованиями

У меня есть имя файла с 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).

11 голосов
/ 12 января 2009

Вот как бы я это сделал:

FN=someletters_12345_moreleters.ext
[[ $FN =~ _([[:digit:]]{5})_ ]] && NUM=${BASH_REMATCH[1]}

Примечание: приведенное выше является регулярным выражением и ограничено вашим конкретным сценарием из пяти цифр, окруженных подчеркиванием. Измените регулярное выражение, если вам нужно другое соответствие.

10 голосов
/ 05 августа 2014

Если мы сосредоточимся на концепции:
«Набор из (одной или нескольких) цифр»

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

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

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 для больших файлов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...