web-dev-qa-db-fra.com

Fractionner une chaîne, extraire des caractères et les reconstituer

J'ai des chaînes sous la forme de wva/sia/e1, bct/e2, sv/de/e11. C'est toujours <Part1>/e<NUM> ou <Part1>/<Part2>/e<NUM>. Ce que je veux, c'est raccourcir les chaînes en conservant les premières lettres des parties et en amerrissant les barres obliques et e:

wva/sia/e1 > ws1
bct/e2 > b2
sv/de/e11 > sd11

Comment puis-je faire cela dans un script sh?

Edit: La chaîne représente un nom de travail:

[...]
job_name=$1 # e.g. 'wva/sia/e1'
job_name=cut_name(job_name) # e.g. 'ws1'
[...]
4
user1406177

Sous la forme d'un script comme ce que vous demandez:

#!/usr/bin/env python3
import sys

# read the input, split by /
st = sys.argv[1].split("/")
# get the first char of all sections *but* the last one
# add the last *from* the first character
print("".join([s[0] for s in st][:-1])+st[-1][1:])

Notez que cela fonctionne pour n'importe quelle longueur, par exemple:

wva/sia/bct/wva/sia/e1

va devenir

wsbws1

tant que la dernière section se termine par /e<num>

Utiliser

  1. Copiez le script dans un fichier vide, enregistrez-le sous le nom rearrange.py
  2. Exécutez-le avec la chaîne comme argument, par exemple:

    python3 /path/to/rearrange.py wva/sia/e1
    
    > ws1
    

Explication

Le script s'explique à peu près tout seul, mais est également commenté.

5
Jacob Vlijm

Bash 4.3 One-Liner

Disons simplement que nous n'avons pas besoin d'un script complet. Bash a suffisamment de capacités pour nous permettre de nous en sortir avec une seule ligne. En voici un:

bash-4.3$ (read -r var ;IFS='/'; printf "%c" ${var%/*};echo ${var##*[^0-9]}) <<<  "sv/de/e11"
sd11

Qu'est-ce qui se passe ?

  • tout se passe dans le sous-shell, d'où ( ) autour de la commande entière
  • nous utilisons ici la chaîne <<< pour envoyer une entrée, et la commande subshell l'obtient via read -r var et la stocke dans la variable var
  • nous plaçons IFS='/' de sorte que le sous-shell divise var en éléments distincts dans le séparateur /. Ceci est important pour le fractionnement de Word.
  • ensuite, nous utilisons la suppression de suffixe ${var%/*} pour supprimer la dernière partie avant /. Dans l'exemple ci-dessus, il s'agirait de e11
  • printf "%c" verra le résultat de ${var%/*} comme sv de en raison du fractionnement du mot et de la suppression du suffixe mentionnés ci-dessus (magie, oui). En raison de la façon dont printf mots, %c n’imprimera que le premier caractère, mais il le fera pour chaque argument de ligne de commande qu’il reçoit. Ainsi, pour sv de, il affichera s et d. L'impression est faite sans nouvelle ligne, donc il semble que les caractères sont entrés dans l'ordre
  • echo ${var##*[^0-9]} utilise la suppression de préfixe pour supprimer tous les caractères non numériques de la chaîne d'entrée donnée, obtenant ainsi uniquement les derniers chiffres.

Il existe une autre approche unique, qui est un peu plus explicite et naturelle pour les programmeurs de type C.

bash-4.3$ (read -r inp;IFS='/';arr=( $inp ); for ((i=0;i<$(( ${#arr[@]} -1 ));i++));do printf "%s" ${arr[$i]:0:1};done;printf "%s\n" ${inp##*[^0-9]}) <<<  "sv/de/e11"
sd11

Quelle est cette magie? Voici une explication:

  • Tout se passe dans le sous-shell, donc () autour de la commande entière.
  • Nous utilisons ici la chaîne <<< pour envoyer l'élément souhaité dans le flux stdin de la commande. La commande l'obtient via la commande read -r inp et le stocke dans la variable inp
  • Ensuite, nous changeons la variable IFS afin de pouvoir tout décomposer en un tableau.
  • nous parcourons tous les éléments jusqu'à celui d'avant le dernier en utilisant le style C pour la boucle for ((initial condition; test condition; post condition)) ; do ... done
  • la $(( ${#arr[@]} - 1 )) est un développement arithmétique dans lequel on soustrait 1 de la longueur du tableau ${#arr[@]}
  • le printf "%s" ${arr[$i]:0:1} nous permet d'utiliser le développement de paramètres dans lequel nous n'imprimons que le premier caractère de chaque élément, et printf "%s" l'imprime sans nouvelle ligne, ainsi il semble que nous imprimions chaque lettre sur la même ligne.
  • enfin, une fois la boucle terminée, nous prenons le texte d'entrée original et nous débarrassons de tout ce qui n'est pas numérique en utilisant la suppression du préfixe ${#*[^0-9]}

Approche script

Puisque la question demande un script Shell, en voici un dans bash 4.3, qui est presque la même approche que ci-dessus, mais plus explicite:

#!/bin/bash
IFS='/'
items=( $1 )
counter=1
for i in ${items[@]}
do
    if [ $counter -eq ${#items[@]}  ];
    then
        # note the space before -1
        printf "%s\n" "${i##*[^0-9]}"
    else
        printf "%s" "${i:0:1}"
    fi
    counter=$(($counter + 1)) 
done

La façon dont cela fonctionne est comme suit:

  • étant donné une chaîne sur la ligne de commande en tant qu'argument, nous plaçons le séparateur de champ interne sur / et autorisons bash à fractionner le mot afin de décomposer la chaîne en un tableau appelé items
  • nous parcourons tous les éléments du tableau ${items[@]} tout en gardant une trace de l'élément auquel nous nous trouvons à l'aide de la variable counter et connaissant le nombre d'éléments du tableau (la partie ${#items[@]}).
  • Le if-statement est ce qui nous permet de choisir un caractère spécifique dans chaque élément. En utilisant le paramètre de développement, premier caractère via${i:0:1}. En utilisant la suppression du préfixe le plus long ${variable##prefix}, nous supprimons tous les caractères non numériques de la dernière chaîne de printf "%s\n" "${i##*[^0-9]}".

Ici c'est en action:

$ ./shorten_string.sh "wva/sia/e1"                         
ws1
$ ./shorten_string.sh "bct/e2"                             
b2
$ ./shorten_string.sh  "sv/de/e11"                     
sd11
3

OK, pas un script, mais vous pouvez le mettre dans un script (cela aussi est très inélégant puisque je n'ai pas réussi à traiter les deux formulaires avec une seule commande)

$ sed -r 's:(.).*/(.).*/e([0-9]+):\1\2\3:;s:(.).*/e([0-9]+):\1\2:' file
ws1
b2
sd11

Remarques

  • -r utilise ERE
  • s:old:new: remplace old par new
  • .* un nombre quelconque de caractères
  • (.) enregistrer un caractère dans cette position
  • ([0-9]+) enregistrer au moins un chiffre ici
  • ; sépare les commandes, comme dans le shell
  • \1 référence arrière aux caractères enregistrés avec ()
1
Zanna