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

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

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

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

Ответ 1

Используйте cut:

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

Более общий:

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

Ответ 2

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

b=${a:12:5}

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

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

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

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

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

Ответ 3

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

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:]]')

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

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

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

Ответ 9

Следуя требованиям

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

Ответ 10

Без каких-либо подпроцессов вы можете:

shopt -s extglob
front=${input%%_+([a-zA-Z]).*}
digits=${front##+([a-zA-Z])_}

Очень маленький вариант этого также будет работать в ksh93.

Ответ 11

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

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

(1): быстрее, чем вызов внешнего инструмента для каждого короткого текста. Не быстрее, чем вся обработка внутри sed или awk для больших файлов.

Ответ 12

Здесь префикс-суффиксное решение (похожее на решения, заданные 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

Ответ 13

Мне нравится 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_ubstitute
  • [0-9]+ соответствует 1 + цифрам
  • \1 ссылки на группу n.1 вывода регулярного выражения (группа 0 - это полное совпадение, в этом случае группа 1 является совпадением в круглых скобках)
  • p флаг для _p_rinting

Все escape-последовательности \ предназначены для обработки обработки sed regexp.

Ответ 14

Данный файл 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

Ответ 15

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

Сделайте некоторые эксперименты самостоятельно, и вы найдете это интересным.

Ответ 16

похож на substr ('abcdefg', 2-1, 3) в php:

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

Ответ 17

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

filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345

Ответ 18

Также существует команда bash builtin 'expr':

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

Ответ 19

Немного поздно, но я просто столкнулся с этой проблемой и нашел следующее:

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

Ответ 20

A bash решение:

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

Это скроет переменную с именем x. Var x может быть изменен на var _.

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