extraire une partie d'une chaîne en utilisant bash / cut / split

121

J'ai une chaîne comme celle-ci:

/var/cpanel/users/joebloggs:DNS9=domain.com

J'ai besoin d'extraire le nom d'utilisateur ( joebloggs) de cette chaîne et de le stocker dans une variable.

Le format de la chaîne sera toujours le même à l'exception de joebloggset domain.comdonc je pense que la chaîne peut être divisée deux fois en utilisant cut?

Le premier fractionnement serait divisé par :et nous stockions la première partie dans une variable pour passer à la deuxième fonction de fractionnement.

La deuxième division serait divisée par /et stockerait le dernier mot ( joebloggs) dans une variable

Je sais comment faire cela en php en utilisant des tableaux et des splits mais je suis un peu perdu dans bash.

Craig Edmonds
la source

Réponses:

333

Pour extraire joebloggsde cette chaîne dans bash en utilisant l'expansion des paramètres sans aucun processus supplémentaire ...

MYVAR="/var/cpanel/users/joebloggs:DNS9=domain.com" 

NAME=${MYVAR%:*}  # retain the part before the colon
NAME=${NAME##*/}  # retain the part after the last slash
echo $NAME

Cela ne dépend pas d' joebloggsêtre à une profondeur particulière du chemin.


Résumé

Un aperçu de quelques modes d'expansion des paramètres, pour référence ...

${MYVAR#pattern}     # delete shortest match of pattern from the beginning
${MYVAR##pattern}    # delete longest match of pattern from the beginning
${MYVAR%pattern}     # delete shortest match of pattern from the end
${MYVAR%%pattern}    # delete longest match of pattern from the end

Cela #signifie donc correspondre depuis le début (pensez à une ligne de commentaire) et %signifie depuis la fin. Une instance signifie la plus courte et deux instances la plus longue.

Vous pouvez obtenir des sous-chaînes en fonction de la position à l'aide de nombres:

${MYVAR:3}   # Remove the first three chars (leaving 4..end)
${MYVAR::3}  # Return the first three characters
${MYVAR:3:5} # The next five characters after removing the first 3 (chars 4-9)

Vous pouvez également remplacer des chaînes ou des modèles particuliers en utilisant:

${MYVAR/search/replace}

Le patternest dans le même format que la correspondance de nom de fichier, donc *(tous les caractères) est commun, souvent suivi d'un symbole particulier comme /ou.

Exemples:

Étant donné une variable comme

MYVAR="users/joebloggs/domain.com" 

Supprimez le chemin en laissant le nom du fichier (tous les caractères jusqu'à une barre oblique):

echo ${MYVAR##*/}
domain.com

Supprimez le nom du fichier en laissant le chemin (supprimez la correspondance la plus courte après la dernière /):

echo ${MYVAR%/*}
users/joebloggs

Obtenez juste l'extension de fichier (supprimez tout avant la dernière période):

echo ${MYVAR##*.}
com

REMARQUE: pour effectuer deux opérations, vous ne pouvez pas les combiner, mais devez les affecter à une variable intermédiaire. Donc, pour obtenir le nom du fichier sans chemin ni extension:

NAME=${MYVAR##*/}      # remove part before last slash
echo ${NAME%.*}        # from the new var remove the part after the last period
domain
beroe
la source
Je ne sais pas s'il s'agit d'un argument pour ou contre l'utilisation créative de grep, mais essayez-le avec VAR = / here / is / a / path: with / a / colon / inside: DNS9 = domain.com
rici
2
Doux! Et cela se fait à l'intérieur du shell en cours d'exécution, donc bien plus rapidement que ceux utilisant d'autres commandes.
stolsvik
3
@Fadi Vous devez changer le caractère générique pour qu'il vienne avant les deux-points et utiliser à la #place de %. Si vous ne voulez que la partie après le tout dernier deux-points, utilisez ${MYVAR##*:}pour obtenir la partie après le premier deux-points, utilisez${MYVAR#*:}
beroe
4
Ami, tu ne sais pas combien de fois je suis revenu sur cette réponse. Je vous remercie!
Joel B
1
Très bonne réponse! Question: Si mon modèle était une variable, est-ce que je le taperais comme ceci ${RET##*$CHOP}ou comme ceci ${RET##*CHOP}(ou d'une autre manière)? EDIT: Semble être l'ancien,${RET##*$CHOP}
Ctrl S
43

Définissez une fonction comme celle-ci:

getUserName() {
    echo $1 | cut -d : -f 1 | xargs basename
}

Et passez la chaîne comme paramètre:

userName=$(getUserName "/var/cpanel/users/joebloggs:DNS9=domain.com")
echo $userName
Stefano Sanfilippo
la source
1
Cette réponse m'a aidé à réaliser ce pour quoi je suis venu ici. Il n'y a pas de réponses acceptées et celle-ci obtient mon vote pour la simplicité.
harperville
1
La seule correction que j'avais à faire dans la commande ci-dessus était de supprimer le «:», comme ceci echo $1 | cut -d -f 1 | xargs. +1 pour des ans simples et soignés.
Bhushan
20

Et pour sed? Cela fonctionnera en une seule commande:

sed 's#.*/\([^:]*\).*#\1#' <<<$string
  • Les #sont utilisés pour les diviseurs de regex au lieu de /puisque la chaîne contient /.
  • .*/ attrape la chaîne jusqu'à la dernière barre oblique inverse.
  • \( .. \)marque un groupe de capture. C'est ça \([^:]*\).
    • Le [^:]dit tout caractère _ sauf un deux-points, et le *signifie zéro ou plus.
  • .* signifie le reste de la ligne.
  • \1signifie remplacer ce qui a été trouvé dans le premier (et unique) groupe de capture. Tel est le nom.

Voici la répartition correspondant à la chaîne avec l'expression régulière:

        /var/cpanel/users/           joebloggs  :DNS9=domain.com joebloggs
sed 's#.*/                          \([^:]*\)   .*              #\1       #'
David W.
la source
Super belle dissection!
kyb
11

Utiliser un seul sed

echo "/var/cpanel/users/joebloggs:DNS9=domain.com" | sed 's/.*\/\(.*\):.*/\1/'
Yann Moisan
la source
10

En utilisant un seul Awk:

... | awk -F '[/:]' '{print $5}'

Autrement dit, en utilisant comme séparateur de champ soit /ou :, le nom d'utilisateur est toujours dans le champ 5.

Pour le stocker dans une variable:

username=$(... | awk -F '[/:]' '{print $5}')

Une implémentation plus flexible sedqui ne nécessite pas que le nom d'utilisateur soit le champ 5:

... | sed -e s/:.*// -e s?.*/??

Autrement dit, supprimez tout ce qui se trouve à partir de :et au-delà, puis supprimez tout jusqu'au dernier /. sedest probablement plus rapide aussi awk, donc cette alternative est certainement meilleure.

Janos
la source