Извлечь подстроку в 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:]]')
в случае, если кто-то хочет более строгой информации, вы также можете искать его в 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
все убегало \
сделать sed
regexp обработки работы.
данного теста.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
мой ответ будет иметь больше контроля над тем, что вы хотите из своей строки. Вот код о том, как вы можете извлечь 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"