Gegeven een bestandsnaam in de vorm someletters_12345_moreleters.ext
, wil ik de 5 cijfers extraheren en in een variabele stoppen.
Dus om het punt te benadrukken, ik heb een bestandsnaam met een x aantal tekens, dan een reeks van vijf cijfers, omringd door een enkel onderstrepingsteken aan weerszijden en dan nog een set van x aantal tekens. Ik wil het 5-cijferige nummer nemen en dat in een variabele zetten.
Ik ben erg geïnteresseerd in het aantal verschillende manieren waarop dit kan worden bereikt.
Antwoord 1, autoriteit 100%
Gebruik knippen:
echo 'someletters_12345_moreleters.ext' | cut -d'_' -f 2
Meer algemeen:
INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING
Antwoord 2, autoriteit 90%
Als aconstant is, voert de volgende parameteruitbreiding substringextractie uit:
b=${a:12:5}
waarbij 12de offset is (op nul gebaseerd) en 5de lengte is
Als de onderstrepingstekens rond de cijfers de enige zijn in de invoer, kunt u het voor- en achtervoegsel (respectievelijk) in twee stappen verwijderen:
tmp=${a#*_} # remove prefix ending in "_"
b=${tmp%_*} # remove suffix starting with "_"
Als er andere onderstrepingstekens zijn, is het waarschijnlijk hoe dan ook haalbaar, zij het lastiger. Als iemand weet hoe beide uitbreidingen in een enkele uitdrukking kunnen worden uitgevoerd, zou ik dat ook graag willen weten.
Beide gepresenteerde oplossingen zijn pure bash, zonder spawning, dus erg snel.
Antwoord 3, Autoriteit 13%
Generieke oplossing waarbij het nummer overal in de bestandsnaam kan zijn, met behulp van de eerste van dergelijke sequenties:
number=$(echo $filename | egrep -o '[[:digit:]]{5}' | head -n1)
Een andere oplossing om exact een deel van een variabele te extraheren:
number=${filename:offset:length}
Als uw bestandsnaam altijd het formaat heeft stuff_digits_...
U kunt AWK gebruiken:
number=$(echo $filename | awk -F _ '{ print $2 }')
Nog een andere oplossing om alles te verwijderen, behalve cijfers, gebruik
number=$(echo $filename | tr -cd '[[:digit:]]')
Antwoord 4, Autoriteit 13%
Probeer gewoon cut -c startIndx-stopIndx
Antwoord 5, Autoriteit 5%
Hier is hoe ik het zou doen:
FN=someletters_12345_moreleters.ext
[[ ${FN} =~ _([[:digit:]]{5})_ ]] && NUM=${BASH_REMATCH[1]}
Uitleg:
bash-specifiek:
[[ ]]
geeft een voorwaardelijke uitdrukking aan=~
geeft aan dat de voorwaarde een reguliere uitdrukking is&&
Kettingen De opdrachten als de PRIORD-opdracht was succesvol
Regelmatige uitdrukkingen (RE): _([[:digit:]]{5})_
_
BENT LITERALEN OM TE DEMMEN / ANKER MATCHING Grenzen voor de snaar die is gekoppeld()
Maak een Capture Group[[:digit:]]
is een tekenklasse, ik denk dat het voor zichzelf spreekt{5}
betekent precies vijf van het eerdere karakter, klasse (zoals in dit voorbeeld) of een groep moet overeenkomen met
In het Engels, kunt u eraan bedenken dat deze zich zo gedraagt: de FN
String is geopenbaard karakter door teken totdat we een _
zien op welk punt de Capture Group geopend en we proberen vijf cijfers te evenaren. Als dat matching succesvol is op dit punt, bespaart de Capture Group de vijf cijfers die doorkruist. Als het volgende teken een _
is, is de voorwaarde succesvol, de Capture Group wordt beschikbaar in BASH_REMATCH
, en de volgende NUM=
Verklaring uitvoeren. Als een deel van de matching mislukt, zijn opgeslagen details weggegooid en het karakter door de verwerking van het teken gaat verder na de _
. b.v. Indien FN
waar _1 _12 _123 _1234 _12345_
, zijn er vier valse starts voordat deze een overeenkomst heeft gevonden.
Antwoord 6, Autoriteit 5%
In het geval dat iemand meer rigoureuze informatie wil, kunt u het ook doorzoeken in Man Bash zoals deze
$ man bash [press return key]
/substring [press return key]
[press "n" key]
[press "n" key]
[press "n" key]
[press "n" key]
Resultaat:
$ {parameter: offset} $ {parameter: offset: lengte} Expansie substring. Breidt uit tot maximaal lengte tekens van Parameter vanaf het teken opgegeven door offset. Indien Lengte is weggelaten, breidt uit naar de substring van parameter start- op het teken gespecificeerd door offset. lengte en offset zijn: rekenkundige uitdrukkingen (zie ARITHMETIC EVALUATION hieronder). Indien offset evalueert tot een getal kleiner dan nul, de waarde wordt gebruikt als een offset vanaf het einde van de waarde van de parameter. Rekenkundig uitdrukkingen die beginnen met een - moeten worden gescheiden door witruimte van het voorgaande : te onderscheiden van de Use Default Uitbreiding van waarden. Als lengte resulteert in een getal kleiner dan nul, en parameter is niet @ en niet geïndexeerd of associatief array, wordt het geïnterpreteerd als een offset vanaf het einde van de waarde parameter in plaats van een aantal tekens, en de expan‐ sion zijn de tekens tussen de twee offsets. Als parameter is @, het resultaat is lengte positionele parameters beginnend bij off‐ set. Als parameter een geïndexeerde arraynaam is, gesubscript door @ of *, het resultaat is de lengte van de leden van de array die begint met ${parameter[offset]}. Er wordt een negatieve offset genomen ten opzichte van één groter dan de maximale index van de opgegeven array. Sub- tekenreeksuitbreiding toegepast op een associatieve array produceert unde‐ boete resultaten. Merk op dat een negatieve offset moet worden gescheiden van de dubbele punt met ten minste één spatie om verwarring te voorkomen met de :- uitbreiding. Indexering van subtekenreeksen is op nul gebaseerd, tenzij de positionele parameters worden gebruikt, in welk geval de indexering begint standaard bij 1. Als offset 0 is, en de positionele parameters worden gebruikt, wordt $0 voorafgegaan door de lijst.
Antwoord 7, autoriteit 3%
Het verbaast me dat deze pure bash-oplossing niet is bedacht:
a="someletters_12345_moreleters.ext"
IFS="_"
set $a
echo $2
# prints 12345
U wilt waarschijnlijk IFS resetten naar de waarde die het eerder was, of unset IFS
daarna!
Antwoord 8, autoriteit 2%
Voortbouwend op het antwoord van jor (wat niet werkt voor mij):
substring=$(expr "$filename" : '.*_\([^_]*\)_.*')
Antwoord 9, autoriteit 2%
Als we ons concentreren op het concept van:
“Een reeks van (een of meerdere) cijfers”
We kunnen verschillende externe tools gebruiken om de getallen te extraheren.
We kunnen vrij gemakkelijk alle andere tekens wissen, ofwel sed of tr:
name='someletters_12345_moreleters.ext'
echo $name | sed 's/[^0-9]*//g' # 12345
echo $name | tr -c -d 0-9 # 12345
Maar als $name meerdere reeksen getallen bevat, zal het bovenstaande mislukken:
Als “name=someletters_12345_moreleters_323_end.ext”, dan:
echo $name | sed 's/[^0-9]*//g' # 12345323
echo $name | tr -c -d 0-9 # 12345323
We moeten reguliere expressies (regex) gebruiken.
Om alleen de eerste run (12345 niet 323) in sed en perl te selecteren:
echo $name | sed 's/[^0-9]*\([0-9]\{1,\}\).*$/\1/'
perl -e 'my $name='$name';my ($num)=$name=~/(\d+)/;print "$num\n";'
Maar we kunnen het net zo goed direct in bash(1)doen:
regex=[^0-9]*([0-9]{1,}).*$; \
[[ $name =~ $regex ]] && echo ${BASH_REMATCH[1]}
Hierdoor kunnen we de EERSTE reeks cijfers van elke lengte extraheren
omgeven door andere tekst/tekens.
Opmerking: regex=[^0-9]*([0-9]{5,5}).*$;
komt slechts exact overeen met 5 cijfers loopt. 🙂
(1): sneller dan het aanroepen van een externe tool voor elke korte tekst. Niet sneller dan alle verwerking binnen sed of awk voor grote bestanden.
Antwoord 10
Voldoet aan de vereisten
Ik heb een bestandsnaam met x aantal tekens en vervolgens vijf cijfers
reeks omgeven door een enkel onderstrepingsteken aan weerszijden en dan een andere
set van x aantal tekens. Ik wil het 5-cijferige nummer nemen en
zet dat in een variabele.
Ik heb enkele grep
manieren gevonden die nuttig kunnen zijn:
$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]+"
12345
of beter
$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]{5}"
12345
En dan met -Po
syntaxis:
$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d+'
12345
Of als je het precies in 5 tekens wilt laten passen:
$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d{5}'
12345
Ten slotte, om het in een variabele op te slaan, hoeft u alleen maar de syntaxis var=$(command)
te gebruiken.
Antwoord 11
Zonder subprocessen kunt u:
shopt -s extglob
front=${input%%_+([a-zA-Z]).*}
digits=${front##+([a-zA-Z])_}
Een zeer kleine variant hiervan werkt ook in ksh93.
Antwoord 12
Hier is een prefix-suffix-oplossing (vergelijkbaar met de oplossingen van JB en Darron) die overeenkomt met het eerste blok cijfers en niet afhankelijk is van de omringende underscores:
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
Antwoord 13
Ik ben dol op het vermogen van sed
om met regex-groepen om te gaan:
> var="someletters_12345_moreletters.ext"
> digits=$( echo $var | sed "s/.*_\([0-9]\+\).*/\1/p" -n )
> echo $digits
12345
Een iets algemenere optie zou zijn om nietaan te nemen dat je een onderstrepingsteken _
hebt dat het begin van je cijferreeks markeert, en dus bijvoorbeeld alle niet-nummers verwijdert je krijgt voor je reeks: 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.
Meer hierover, voor het geval je niet al te zeker bent van regexps:
s
is voor _s_ubstitute[0-9]+
komt overeen met 1+ cijfers\1
linkt naar groep n.1 van de regex-uitvoer (groep 0 is de hele match, groep 1 is in dit geval de match tussen haakjes)p
vlag is voor _p_rinting
Alle escapes \
zijn er om de regexp-verwerking van sed
te laten werken.
Antwoord 14
Mijn antwoord heeft meer controle over wat je uit je string wilt halen. Hier is de code over hoe je 12345
uit je string kunt extraheren
str="someletters_12345_moreleters.ext"
str=${str#*_}
str=${str%_more*}
echo $str
Dit is efficiënter als je iets wilt extraheren met tekens zoals abc
of speciale tekens zoals _
of -
. Bijvoorbeeld: als je string zo is en je wilt alles wat na someletters_
en voor _moreleters.ext
staat:
str="someletters_123-45-24a&13b-1_moreleters.ext"
Met mijn code kun je aangeven wat je precies wilt.
Uitleg:
#*
Het verwijdert de voorgaande string inclusief de bijbehorende sleutel. Hier is de sleutel die we noemden _
%
Het zal de volgende string inclusief de bijbehorende sleutel verwijderen. Hier is de sleutel die we noemden ‘_more*’
Doe zelf wat experimenten en je zou dit interessant vinden.
Antwoord 15
Gezien test.txt is een bestand met “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
Antwoord 16
shell cut – print een specifiek bereik van karakters of een bepaald deel van een string
#methode1) met bash
str=2020-08-08T07:40:00.000Z
echo ${str:11:8}
#methode2) met knippen
str=2020-08-08T07:40:00.000Z
cut -c12-19 <<< $str
#method3) bij het werken met awk
str=2020-08-08T07:40:00.000Z
awk '{time=gensub(/.{11}(.{8}).*/,"\\1","g",$1); print time}' <<< $str
Antwoord 17
vergelijkbaar met substr(‘abcdefg’, 2-1, 3) in php:
echo 'abcdefg'|tail -c +2|head -c 3
Antwoord 18
Ok, hier gaat pure parametervervanging met een lege string. Voorbehoud is dat ik sommige lettersen meerlettersheb gedefinieerd als alleen tekens. Als ze alfanumeriek zijn, werkt dit niet zoals het is.
filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345
Antwoord 19
Er is ook het bash-ingebouwde ‘expr’-commando:
INPUT="someletters_12345_moreleters.ext"
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `
echo $SUBSTRING
Antwoord 20
Een beetje laat, maar ik kwam dit probleem net tegen en vond het volgende:
host:/tmp$ asd=someletters_12345_moreleters.ext
host:/tmp$ echo `expr $asd : '.*_\(.*\)_'`
12345
host:/tmp$
Ik heb het gebruikt om milliseconden resolutie te krijgen op een ingebed systeem dat geen %N voor datum heeft:
set `grep "now at" /proc/timer_list`
nano=$3
fraction=`expr $nano : '.*\(...\)......'`
$debug nano is $nano, fraction is $fraction
Antwoord 21
Een bash-oplossing:
IFS="_" read -r x digs x <<<'someletters_12345_moreleters.ext'
Hiermee wordt een variabele met de naam x
gekloond. De var x
kan worden gewijzigd in de var _
.
input='someletters_12345_moreleters.ext'
IFS="_" read -r _ digs _ <<<"$input"
Antwoord 22
Inclusief einde, vergelijkbaar met JS- en Java-implementaties. Verwijder +1 als je dit niet wenst.
function substring() {
local str="$1" start="${2}" end="${3}"
if [[ "$start" == "" ]]; then start="0"; fi
if [[ "$end" == "" ]]; then end="${#str}"; fi
local length="((${end}-${start}+1))"
echo "${str:${start}:${length}}"
}
Voorbeeld:
substring 01234 0
01234
substring 012345 0
012345
substring 012345 0 0
0
substring 012345 1 1
1
substring 012345 1 2
12
substring 012345 0 1
01
substring 012345 0 2
012
substring 012345 0 3
0123
substring 012345 0 4
01234
substring 012345 0 5
012345
Meer voorbeeldoproepen:
substring 012345 0
012345
substring 012345 1
12345
substring 012345 2
2345
substring 012345 3
345
substring 012345 4
45
substring 012345 5
5
substring 012345 6
substring 012345 3 5
345
substring 012345 3 4
34
substring 012345 2 4
234
substring 012345 1 3
123