Wyodrębnij podłańcuch w Bash

Biorąc pod uwagę nazwę pliku w postaci someletters_12345_moreleters.ext, chcę wyodrębnić 5 cyfr i umieścić je w zmiennej.

Więc aby podkreślić punkt, mam nazwę pliku z X liczba znaków następnie Sekwencja pięciocyfrowa otoczona pojedynczym podkreśleniem po obu stronach, a następnie inny zestaw x liczba znaków. Chcę wziąć 5-cyfrowy numer i umieścić go w zmiennej.

Jestem bardzo zainteresowany liczbą różnych sposobów, jakie można to osiągnąć.

Author: codeforester, 2009-01-09

20 answers

Użycie Cięcie :

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

Bardziej ogólne:

INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING
 528
Author: FerranB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-04 22:51:55

Jeśli x jest stała, to następujący parametr expansion wykonuje ekstrakcję podłańcucha:

b=${a:12:5}

Gdzie 12 jest offsetem (opartym o zero) i 5 jest długością

Jeśli podkreślniki wokół cyfr są jedynymi w danych wejściowych, możesz usunąć prefiks i sufiks (odpowiednio) w dwóch krokach:

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

Jeśli są inne podkreślenia, prawdopodobnie i tak jest to wykonalne, choć bardziej skomplikowane. Jeśli ktoś wie jak wykonać oba rozszerzenia w jedno wyrażenie, też chciałbym wiedzieć.

Oba przedstawione rozwiązania to czysty bash, bez procesu spawania, stąd bardzo szybki.

 792
Author: JB.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-12 17:54:17

Rozwiązanie ogólne, gdzie liczba może znajdować się w dowolnym miejscu w nazwie pliku, używając pierwszego z takich sekwencji:

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

Inne rozwiązanie do wyodrębnienia dokładnie części zmiennej:

number=${filename:offset:length}

Jeśli nazwa pliku ma zawsze format stuff_digits_... możesz użyć awk:

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

Yet another solution to remove everything oprócz cyfr, use

number=$(echo $filename | tr -cd '[[:digit:]]')
 75
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-09 14:25:34

Po prostu spróbuj użyć cut -c startIndx-stopIndx

 63
Author: brown.2179,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-22 17:54:15

W przypadku, gdy ktoś chce bardziej rygorystyczne informacje, można również wyszukać je w Man bash jak to

$ man bash [press return key]
/substring  [press return key]
[press "n" key]
[press "n" key]
[press "n" key]
[press "n" key]

Wynik:

${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.
 31
Author: jperelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-22 15:16:02

Bazując na odpowiedzi Jora (która mi nie działa):

substring=$(expr "$filename" : '.*_\([^_]*\)_.*')
 17
Author: PEZ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-09 15:46:32

Dziwię się, że to czyste rozwiązanie Basha nie wyszło:

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

Prawdopodobnie chcesz zresetować IFS do wartości, która była wcześniej, lub unset IFS później!

 15
Author: user1338062,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-03 17:34:40

Zgodnie z wymaganiami

Mam nazwę pliku z X liczba znaków następnie pięć cyfr Sekwencja otoczona pojedynczym podkreśleniem po obu stronach, a następnie innym zestaw x liczba znaków. Chcę wziąć 5-cyfrowy numer i umieść to w zmiennej.

Znalazłem kilka grep sposobów, które mogą się przydać:

$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]+" 
12345

Lub lepiej

$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]{5}" 
12345

A następnie z -Po składnia:

$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d+' 
12345

Lub jeśli chcesz, aby pasował dokładnie 5 znaki:

$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d{5}' 
12345

Na koniec, aby była przechowywana w zmiennej, wystarczy użyć składni var=$(command).

 11
Author: fedorqui,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-29 11:50:27

Bez żadnych podprocesów można:

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

Bardzo mały wariant tego będzie działał również w ksh93.

 9
Author: Darron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-09 16:13:38

Jeśli skupimy się na pojęciu:
"Przebieg (jeden lub kilka) cyfr"

Moglibyśmy użyć kilku zewnętrznych narzędzi do wyodrębnienia liczb.
Możemy łatwo wymazać wszystkie inne znaki, sed lub tr:

name='someletters_12345_moreleters.ext'

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

Ale jeśli $name zawiera kilka ciągów liczb, powyższe nie powiedzie się:

If " name=someletters_12345_moreleters_323_end.ext", następnie:

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

Musimy używać regularnych wyrażeń (regex).
Aby wybrać tylko pierwszy bieg (12345 nie 323) w sed i perl:

echo $name | sed 's/[^0-9]*\([0-9]\{1,\}\).*$/\1/'
perl -e 'my $name='$name';my ($num)=$name=~/(\d+)/;print "$num\n";'

Ale równie dobrze możemy to zrobić bezpośrednio w bash(1) :

regex=[^0-9]*([0-9]{1,}).*$; \
[[ $name =~ $regex ]] && echo ${BASH_REMATCH[1]}

To pozwala nam wyodrębnić pierwszy ciąg cyfr o dowolnej długości
otoczona innymi tekstami / znakami.

Uwaga: regex=[^0-9]*([0-9]{5,5}).*$; będzie pasować tylko dokładnie 5 cyfr działa. :-)

(1): szybsze niż wywołanie zewnętrznego narzędzia dla każdego krótkiego tekstu. Nie szybciej niż wykonanie całego przetwarzania wewnątrz sed lub awk dla dużych plików.

 9
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-22 04:25:34

Oto rozwiązanie z prefiksem-sufiksem (podobne do rozwiązań podanych przez JB i Darrona), które pasuje do pierwszego bloku cyfr i nie zależy od otaczających podkreślników:

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
 8
Author: codist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-06 12:50:13

Oto jak bym to zrobił:

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

Uwaga: powyższe wyrażenie jest wyrażeniem regularnym i jest ograniczone do konkretnego scenariusza pięciu cyfr otoczonych podkreślnikami. Zmień Wyrażenie regularne, jeśli potrzebujesz innego dopasowania.

 7
Author: nicerobot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-12 19:43:20

I love sed'S możliwość radzenia sobie z grupami regex:

> var="someletters_12345_moreletters.ext"
> digits=$( echo $var | sed "s/.*_\([0-9]\+\).*/\1/p" -n )
> echo $digits
12345

Nieco bardziej ogólną opcją byłoby a nie założenie, że masz podkreślenie _ oznaczające początek sekwencji cyfr, stąd na przykład usunięcie wszystkich nie-liczb przed sekwencją: 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.

Więcej na ten temat, w przypadku, gdy nie jesteś zbyt pewny regexps:

  • s jest dla _s_ubstitute
  • [0-9]+ mecze 1+ cyfry
  • \1 Linki do grupy n. 1 wyjścia regex (Grupa 0 to cały mecz, Grupa 1 to mecz w nawiasach w tym przypadku)
  • p flaga jest dla _p_rinting

Wszystkie znaki specjalne \ służą do przetwarzania regexp sed.

 4
Author: Campa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-21 07:22:42

Podany test.txt jest plikiem zawierającym "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
 4
Author: Rick Osman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-23 14:21:19

Podobne do substr ('abcdefg', 2-1, 3) w php:

echo 'abcdefg'|tail -c +2|head -c 3
 3
Author: diyism,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-26 11:34:08

Moja odpowiedź będzie miała większą kontrolę nad tym, co chcesz z twojego Sznurka. Oto kod, w jaki sposób możesz wyodrębnić 12345 ze swojego łańcucha

str="someletters_12345_moreleters.ext"
str=${str#*_}
str=${str%_more*}
echo $str

Będzie to bardziej efektywne, jeśli chcesz wyodrębnić coś, co ma jakiekolwiek znaki, takie jak abc lub znaki specjalne, takie jak _ lub -. Na przykład: jeśli twój ciąg jest taki i chcesz wszystko, co jest po someletters_ i przed _moreleters.ext:

str="someletters_123-45-24a&13b-1_moreleters.ext"

Z moim kodem możesz wspomnieć, co dokładnie ty chcę. Explanation:

#* usunie poprzedzający łańcuch wraz z pasującym kluczem. Tutaj klucz, o którym wspomnieliśmy, to _ % usunie następujący ciąg znaków wraz z pasującym kluczem. Tutaj klucz, o którym wspomnieliśmy, to '_more*'

Zrób sam kilka eksperymentów, a to cię zainteresuje.

 3
Author: Alex Raj Kaliamoorthy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-29 07:41:26

Jest też wbudowana Komenda 'expr' Basha:

INPUT="someletters_12345_moreleters.ext"  
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `  
echo $SUBSTRING
 2
Author: jor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-09 16:05:52

Ok, oto Czysta substytucja parametru z pustym łańcuchem. Zastrzeżenie jest takie, że zdefiniowałem someletters i moreletters jako tylko znaki. Jeśli są alfanumeryczne, to nie będzie działać tak, jak jest.

filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345
 2
Author: morbeo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-26 18:04:06

Trochę późno, ale po prostu natknąłem się na ten problem i znalazłem następujące:

host:/tmp$ asd=someletters_12345_moreleters.ext 
host:/tmp$ echo `expr $asd : '.*_\(.*\)_'`
12345
host:/tmp$ 

Użyłem go do uzyskania milisekundowej rozdzielczości na wbudowanym systemie, który nie ma %N Dla daty:

set `grep "now at" /proc/timer_list`
nano=$3
fraction=`expr $nano : '.*\(...\)......'`
$debug nano is $nano, fraction is $fraction
 1
Author: russell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-01 08:12:33

Rozwiązanie bash:

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

Spowoduje to zablokowanie zmiennej o nazwie x. Var x można zmienić na var _.

input='someletters_12345_moreleters.ext'
IFS="_" read -r _ digs _ <<<"$input"
 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-22 05:45:24