Domanda

Dato un nome file nel formato someletters_12345_moreleters.ext , voglio estrarre le 5 cifre e inserirle in una variabile.

Quindi, per enfatizzare il punto, ho un nome file con x numero di caratteri, quindi una sequenza di cinque cifre circondata da un singolo trattino basso su entrambi i lati, quindi un altro set di x numero di caratteri. Voglio prendere il numero di 5 cifre e inserirlo in una variabile.

Sono molto interessato al numero di modi diversi in cui questo può essere realizzato.

È stato utile?

Soluzione

Usa cut :

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

Più generico:

INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING

Altri suggerimenti

Se x è costante, l'espansione dei parametri seguente esegue l'estrazione della sottostringa:

b=${a:12:5}

dove 12 è l'offset (in base zero) e 5 è la lunghezza

Se i caratteri di sottolineatura attorno alle cifre sono gli unici nell'input, puoi rimuovere il prefisso e il suffisso (rispettivamente) in due passaggi:

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

Se ci sono altri caratteri di sottolineatura, è probabilmente fattibile comunque, anche se più complicato. Se qualcuno sa come eseguire entrambe le espansioni in una sola espressione, mi piacerebbe saperlo anche.

Entrambe le soluzioni presentate sono pure bash, senza la generazione di processi, quindi molto veloce.

Soluzione generica in cui il numero può trovarsi ovunque nel nome file, usando la prima di tali sequenze:

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

Un'altra soluzione per estrarre esattamente una parte di una variabile:

number=${filename:offset:length}

Se il tuo nome file ha sempre il formato stuff_digits _... puoi usare awk:

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

Ancora un'altra soluzione per rimuovere tutto tranne le cifre, usare

number=$(echo $filename | tr -cd '[[:digit:]]')

prova a usare cut -c startIndx-stopIndx

Nel caso in cui qualcuno desideri informazioni più rigorose, puoi anche cercarle in man bash in questo modo

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

Risultato:

${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.

Basandosi sulla risposta di jor (che non funziona per me):

substring=$(expr "$filename" : '.*_\([^_]*\)_.*')

Sono sorpreso che questa pura soluzione bash non sia venuta fuori:

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

Probabilmente vuoi ripristinare IFS a quale valore era prima o disinserire IFS in seguito!

Seguendo i requisiti

  

Ho un nome file con x numero di caratteri quindi a cinque cifre   sequenza circondata da un singolo trattino basso su entrambi i lati e poi su un altro   set di x numero di caratteri. Voglio prendere il numero di 5 cifre e   mettilo in una variabile.

Ho trovato alcuni grep che potrebbero essere utili:

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

o migliore

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

E poi con la sintassi -Po :

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

O se vuoi adattarlo esattamente a 5 caratteri:

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

Infine, per farlo archiviare in una variabile è sufficiente usare la sintassi var = $ (comando) .

Ecco come lo farei:

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

Nota: quanto sopra è un'espressione regolare ed è limitato allo scenario specifico di cinque cifre circondato da caratteri di sottolineatura. Cambia l'espressione regolare se hai bisogno di una corrispondenza diversa.

Senza alcun processo secondario puoi:

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

Una variante molto piccola di questo funzionerà anche in ksh93.

Se ci concentriamo sul concetto di:
    " Una serie di (una o più) cifre "

Potremmo usare diversi strumenti esterni per estrarre i numeri.
Potremmo facilmente cancellare tutti gli altri personaggi, sed o tr:

name='someletters_12345_moreleters.ext'

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

Ma se $ name contiene diverse serie di numeri, quanto sopra fallirà:

Se " name = someletters_12345_moreleters_323_end.ext " ;, quindi:

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

Dobbiamo usare le espressioni regolari (regex).
Per selezionare solo la prima corsa (12345 non 323) in sed e perl:

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

Ma potremmo anche farlo direttamente in bash (1) :

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

Questo ci consente di estrarre la PRIMA serie di cifre di qualsiasi lunghezza
circondato da qualsiasi altro testo / carattere.

Nota : regex = [^ 0-9] * ([0-9] {5,5}). * $; corrisponderà esattamente solo a 5 cifre piste. : -)

(1) : più veloce di chiamare uno strumento esterno per ogni breve testo. Non più veloce di eseguire tutta l'elaborazione all'interno di sed o awk per file di grandi dimensioni.

Ecco una soluzione con prefisso-suffisso (simile alle soluzioni fornite da JB e Darron) che corrisponde al primo blocco di cifre e non dipende dai caratteri di sottolineatura circostanti:

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

Adoro la capacità di sed di gestire gruppi di regex:

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

Un'opzione leggermente più generale sarebbe non supporre che tu abbia un carattere di sottolineatura _ che segna l'inizio della sequenza di cifre, quindi ad esempio rimuovendo tutti i non numeri ottieni prima della sequenza: 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.

Altro su questo, nel caso in cui non sei troppo sicuro con regexps:

  • s è per _s_ubstitute
  • [0-9] + corrisponde a 1+ cifre
  • \ 1 si collega al gruppo n.1 dell'output regex (il gruppo 0 è l'intera corrispondenza, in questo caso il gruppo 1 è la corrispondenza tra parentesi)
  • Il flag
  • p è per _p_rinting

Tutte le escape \ sono lì per far funzionare l'elaborazione regexp di sed .

Dato test.txt è un file contenente " 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

La mia risposta avrà un maggiore controllo su ciò che desideri dalla tua stringa. Ecco il codice su come estrarre 12345 dalla tua stringa

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

Questo sarà più efficiente se vuoi estrarre qualcosa che ha caratteri come abc o caratteri speciali come _ o - . Ad esempio: se la tua stringa è così e vuoi tutto ciò che è dopo someletters_ e prima di _moreleters.ext :

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

Con il mio codice puoi menzionare esattamente quello che vuoi. Spiegazione:

# * Rimuoverà la stringa precedente inclusa la chiave corrispondente. Qui la chiave che abbiamo menzionato è _ % Rimuoverà la seguente stringa inclusa la chiave corrispondente. Qui la chiave che abbiamo citato è '_more *'

Fai alcuni esperimenti da solo e lo troverai interessante.

simile a substr ('abcdefg', 2-1, 3) in php:

echo 'abcdefg'|tail -c +2|head -c 3

Ok, ecco la pura sostituzione dei parametri con una stringa vuota. Un avvertimento è che ho definito someletter e moreletter come solo personaggi. Se sono alfanumerici, questo non funzionerà così com'è.

filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345

C'è anche il comando bash incorporato 'expr':

INPUT="someletters_12345_moreleters.ext"  
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `  
echo $SUBSTRING

Un po 'in ritardo, ma ho riscontrato questo problema e ho trovato quanto segue:

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

L'ho usato per ottenere la risoluzione di millisecondi su un sistema incorporato che non ha% N per data:

set `grep "now at" /proc/timer_list`
nano=$3
fraction=`expr $nano : '.*\(...\)......'`
$debug nano is $nano, fraction is $fraction

Una soluzione bash:

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

Questo bloccherà una variabile chiamata x . Var x potrebbe essere cambiato in var _ .

input='someletters_12345_moreleters.ext'
IFS="_" read -r _ digs _ <<<"$input"
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top