Учитывая имя файла в форме someletters_12345_moreleters.ext
, я хочу извлечь 5 цифр и поместить их в переменную.
Итак, чтобы подчеркнуть суть, у меня есть имя файла с числом символов x, а затем пятизначная последовательность, окруженная одним подчеркиванием с обеих сторон, а затем еще один набор из х символов. Я хочу взять 5-значное число и поместить его в переменную.
Меня очень интересует количество различных способов, которыми это может быть достигнуто.
Используйте cut:
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 "_"
Если есть другие подчеркивания, это, вероятно, возможно в любом случае, хотя и более сложное. Если кто-то знает, как выполнять оба расширения в одном выражении, я тоже хотел бы знать.
Оба представленных решения являются чистыми bash, без участия процесса, поэтому очень быстро.
${${a#*_}%_*}
. Я использовал это прежде, чтобы связать воедино несколько операций bash string, чтобы получить определенный раздел подстроки.
bash: ${${a#*_}%_*}: bad substitution
в моем GNU bash 4.2.45.
Общее решение, где число может быть где угодно в имени файла, используя первую из таких последовательностей:
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
startIndx-$((lastIndx-1))
Если кто-то хочет получить более строгую информацию, вы также можете найти его в 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.
${var: -4}
Я удивлен, что это чистое решение bash не появилось:
a="someletters_12345_moreleters.ext"
IFS="_"
set $a
echo $2
# prints 12345
Вероятно, вы хотите reset IFS, какое значение было раньше, или unset IFS
после этого!
IFS
и позиционные параметры: IFS=_ read -r _ digs _ <<< "$a"; echo "$digs"
Основываясь на jor-ответе (который не работает для меня):
substring=$(expr "$filename" : '.*_\([^_]*\)_.*')
cut
его.
Следуя требованиям
У меня есть имя файла с числом символов 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)
.
Invocation as 'egrep' is deprecated; use 'grep -E' instead
. Я отредактировал твой ответ.
Если мы сосредоточимся на концепции:
"Прогон (один или несколько) цифр"
Мы могли бы использовать несколько внешних инструментов для извлечения чисел.
Мы могли бы легко стереть все остальные символы, либо 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 для больших файлов.
Без каких-либо подпроцессов вы можете:
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
Вот как бы я это сделал:
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]\+\).*/\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.
Мой ответ будет иметь больше контроля над тем, что вы хотите от своей строки. Вот код о том, как вы можете извлечь 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 *"
Сделайте некоторые эксперименты самостоятельно, и вы найдете это интересным.
похож на substr ('abcdefg', 2-1, 3) в php:
echo 'abcdefg'|tail -c +2|head -c 3
Данный файл 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
Хорошо, здесь идет чистая замена параметра с пустой строкой. Предостережение заключается в том, что я определил someletters и moreletters как только символы. Если они являются буквенно-цифровыми, это не будет работать так, как есть.
filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345
Также существует команда bash builtin 'expr':
INPUT="someletters_12345_moreleters.ext"
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `
echo $SUBSTRING
expr
не является встроенным.
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"
Немного поздно, но я просто столкнулся с этой проблемой и нашел следующее:
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
abc_12345_def_67890_ghi_def
является допустимым вводом. Что ты хочешь случиться? Давайте предположим, что есть только одна последовательность из 5 цифр. У вас все еще естьabc_def_12345_ghi_jkl
или1234567_12345_1234567
или12345d_12345_12345e
качестве допустимого ввода, основанного на вашем определении ввода, и большинство ответов ниже не справятся с этим.