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ąć.
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
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.
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:]]')
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
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.
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" : '.*_\([^_]*\)_.*')
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!
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)
.
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.
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.
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
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.
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
.
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
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
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.
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
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
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
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"