Substrat d'extraction en Bash

donné un nom de fichier dans la forme someletters_12345_moreleters.ext , je veux extraire les 5 chiffres et les mettre dans une variable.

donc pour souligner le point, j'ai un nom de fichier avec x nombre de caractères puis une séquence à cinq chiffres entourée d'un underscore simple de chaque côté puis un autre ensemble de X nombre de caractères. Je veux prendre le nombre à 5 chiffres et le mettre dans une variable.

je suis très intéressé par le nombre de façons différentes que cela peut être accompli.

521
demandé sur codeforester 2009-01-09 16:53:22

20 réponses

utiliser couper :

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

plus générique:

INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING
528
répondu FerranB 2015-11-04 22:51:55

si x est constant, le paramètre d'expansion suivant effectue l'extraction sur substrat:

b=${a:12:5}

12 est l'offset (base zéro) et 5 est la longueur

si les soulignements autour des chiffres sont les seuls dans l'entrée, vous pouvez enlever le préfixe et le suffixe (respectivement) en deux étapes:

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

S'il y a d'autres souligne, c'est probablement faisable de toute façon, bien que plus délicat. Si quelqu'un sait réaliser les deux extensions en une seule expression, j'aimerais le savoir aussi.

les deux solutions présentées sont pur bash, sans processus de fraie impliqué, donc très rapide.

792
répondu JB. 2016-10-12 17:54:17

solution générique où le numéro peut se trouver n'importe où dans le nom du fichier, en utilisant la première de ces séquences:

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

une Autre solution pour extraire exactement une partie d'une variable:

number=${filename:offset:length}

si votre nom de fichier a toujours le format stuff_digits_... vous pouvez utiliser awk:

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

encore une autre solution pour supprimer tout sauf les chiffres, utiliser

number=$(echo $filename | tr -cd '[[:digit:]]')
75
répondu Johannes Schaub - litb 2009-01-09 14:25:34

essayez simplement d'utiliser cut -c startIndx-stopIndx

63
répondu brown.2179 2010-09-22 17:54:15

dans le cas où quelqu'un veut des informations plus rigoureuses, vous pouvez également le rechercher dans man bash comme ceci

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

résultat:

${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, "151910920" is prefixed to the list.
31
répondu jperelli 2016-01-22 15:16:02

construit sur la réponse de jor (qui ne fonctionne pas pour moi):

substring=$(expr "$filename" : '.*_\([^_]*\)_.*')
17
répondu PEZ 2009-01-09 15:46:32

je suis surpris que cette pure solution de bash ne soit pas venue:

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

vous voulez probablement réinitialiser IFS à sa valeur d'avant, ou unset IFS après!

15
répondu user1338062 2013-06-03 17:34:40

selon les prescriptions

j'ai un nom de fichier avec x nombre de caractères puis un cinq chiffres séquence entouré par un seul trait de soulignement de chaque côté puis de l'autre ensemble de x nombre de caractères. Je veux prendre le numéro à 5 chiffres et mettre dans une variable.

j'ai trouvé quelques grep les moyens qui peuvent être utiles:

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

ou mieux

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

et ensuite avec -Po syntaxe:

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

ou si vous voulez le faire correspondre exactement 5 caractères:

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

Enfin, pour qu'il soit stockée dans une variable, il suffit d'utiliser la "151960920 de syntaxe".

11
répondu fedorqui 2015-07-29 11:50:27

sans aucun sous-processus vous pouvez:

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

une très petite variante de cela fonctionnera également dans ksh93.

9
répondu Darron 2009-01-09 16:13:38

Si nous nous concentrons sur le concept de:

"Une course de (un ou plusieurs) chiffres"

Nous pourrions utiliser plusieurs outils externes pour extraire les numéros.

Nous pourrions facilement effacer tous les autres caractères, soit sed ou tr:

name='someletters_12345_moreleters.ext'

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

mais si $name contient plusieurs tirages de nombres, ce qui précède échouera:

si "nom=someletters_12345_moreleters_323_end.ext", puis:

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

nous devons utiliser des expressions régulières (regex).

Pour sélectionner seulement la première exécution (12345 pas 323) dans sed et perl:

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

mais nous pourrions aussi bien le faire directement à bash (1) :

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

cela nous permet d'extraire la première série de chiffres de n'importe quelle longueur

entouré par tout autre texte/caractères.

Note : regex=[^0-9]*([0-9]{5,5}).*$; ne correspond qu'à des tirages à 5 chiffres. :- )

(1) : plus rapide que d'appeler un outil externe pour chaque texte court. Pas plus vite que de faire tout le traitement à l'intérieur de sed ou awk pour les gros fichiers.

9
répondu 2016-01-22 04:25:34

Voici une solution préfixe-suffixe (semblable aux solutions données par JB et Darron) qui correspond au premier bloc de chiffres et ne dépend pas des soulignements environnants:

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
répondu codist 2011-05-06 12:50:13

Voici comment je ferais:

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

Note: ce qui précède est une expression régulière et se limite à votre scénario spécifique de cinq chiffres entourés de underscores. Changez l'expression régulière si vous avez besoin d'une correspondance différente.

7
répondu nicerobot 2009-01-12 19:43:20

j'aime sed 's la capacité de traiter avec la regex groupes:

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

une option légèrement plus générale serait et non pour supposer que vous avez un underscore _ marquant le début de votre séquence de chiffres, donc par exemple en enlevant tous les non-nombres que vous obtenez avant votre séquence: s/[^0-9]\+\([0-9]\+\).*//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  through  to refer to the corresponding matching sub-expressions in the regexp.

plus sur ce, en cas où vous n'êtes pas trop confiant avec regexps:

  • s est pour _s_ubstitute
  • [0-9]+ correspond à 1 + chiffres
  • liens pour le groupe n.1 de la regex de sortie (groupe 0 est l'ensemble du match, le groupe 1 est le match à l'intérieur de parenthèses dans ce cas)
  • p le drapeau est pour _p_rinting

toutes les évasions \ sont là pour faire le travail de traitement regexp de sed .

4
répondu Campa 2017-03-21 07:22:42

à l'essai.txt est un fichier contenant "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
répondu Rick Osman 2017-03-23 14:21:19

similaire à substr ('abcdefg', 2-1, 3) en php:

echo 'abcdefg'|tail -c +2|head -c 3
3
répondu diyism 2013-06-26 11:34:08

ma réponse aura plus de contrôle sur ce que vous voulez de votre chaîne. Voici le code sur comment vous pouvez extraire 12345 de votre chaîne

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

Ce sera plus efficace si vous voulez extraire quelque chose qui a des caractères comme abc ou de caractères spéciaux comme _ ou - . Par exemple: si votre chaîne est comme ceci et que vous voulez tout ce qui est après someletters_ et avant _moreleters.ext :

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

avec mon code, vous pouvez dire exactement ce que vous voulez. Explication:

#* il supprimera la chaîne précédente y compris la clé correspondante. Ici, la clé que nous avons mentionnée est _ % il supprimera la chaîne suivante, y compris la clé correspondante. Ici, la clé que nous avons mentionnée est '_more*'

faites quelques expériences vous-même et vous trouverez cela intéressant.

3
répondu Alex Raj Kaliamoorthy 2016-07-29 07:41:26

il y a aussi la commande bash builtin 'expr':

INPUT="someletters_12345_moreleters.ext"  
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `  
echo $SUBSTRING
2
répondu jor 2009-01-09 16:05:52

Ok, voici la pure Substitution de paramètre avec une chaîne vide. La mise en garde est que j'ai défini someletters et moreletters comme des caractères seulement. Si ils sont alphanumériques, cela ne fonctionne pas comme il est.

filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345
2
répondu morbeo 2015-11-26 18:04:06

un peu tard, mais je viens de courir à travers ce problème et a trouvé ce qui suit:

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

Je l'ai utilisé pour obtenir une résolution de milliseconde sur un système intégré qui n'a pas de %N pour la date:

set `grep "now at" /proc/timer_list`
nano=
fraction=`expr $nano : '.*\(...\)......'`
$debug nano is $nano, fraction is $fraction
1
répondu russell 2013-08-01 08:12:33

Un bash solution:

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

cela va fracasser une variable appelée x . La var x peut être remplacée par la var _ .

input='someletters_12345_moreleters.ext'
IFS="_" read -r _ digs _ <<<"$input"
1
répondu 2016-01-22 05:45:24