Nom des variables (et définies) dans le shell en cours, en fonction des données d’entrée de ligne

J’ai une sortie SQL * Plus écrite dans un fichier texte au format suivant:

3459906| |2|X1|WAS1| Output1 334596| |2|X1|WAS2| Output1 3495792| |1|X1|WAS1| Output1 687954| |1|X1|WAS2| Output1 

J’ai besoin d’un script shell pour récupérer les comptes qui étaient au début basés sur le texte après les comptes.

Par exemple, si le texte est comme |2|X1|WAS1| , alors 3459906 devrait être transmis à une variable x1was12 et si le texte est comme |2|X1|WAS2| , alors 334596 devrait être transmis à une variable x1was22 .

J’ai essayé d’écrire une boucle for et if condition pour transmettre les comptes, mais a échoué:

 export filename1='file1.dat' while read -r line ; do if [[ grep -i "*|2|X1|WAS1| Output1*" | wc -l -eq 0 ]] ; then export xwas12=sed -n ${line}p $filename1 | \ sed 's/[^0-9]*//g' | sed 's/..$//' elif [[ grep -i "*|2|X1|WAS2| Output1*" | wc -l -eq 0 ]] ; then export x1was22=sed -n ${line}p $filename1 | \ sed 's/[^0-9]*//g' | sed 's/..$//' elif [[ grep -i "*|1|X1|WAS1| Output1*" | wc -l -eq 0 ]] ; then export x1was11=sed -n ${line}p $filename1 | \ sed 's/[^0-9]*//g' | sed 's/..$//' elif [[ grep -i "*|1|X1|WAS2| Output1*" | wc -l -eq 0 ]] export x1was21=sed -n ${line}p $filename1 | \ sed 's/[^0-9]*//g' | sed 's/..$//' fi done  output.txt echo '$x1was22' >> output.txt echo '$x1was11' >> output.txt echo '$x1was21' >> output.txt 

Ce que j’essayais de faire était:

  1. Aller à la première ligne du fichier
    • -> Recherchez le texte et, le cas échéant, affectez la sortie sed à la variable
  2. Ensuite, allez à la deuxième ligne du fichier
    • -> Recherchez les textes dans les commandes if et affectez la sortie sed à une autre variable.
  3. même chose pour les autres

 while IFS='|' read -r count _ nx was _; do # remove spaces from all variables count=${count// /}; n=${n// /}; x=${x// /}; was=${was// /} varname="${x}${was}${n}" printf -v "${varname,,}" %s "$count" done <<'EOF' 3459906| |2|X1|WAS1| Output1 334596| |2|X1|WAS2| Output1 3495792| |1|X1|WAS1| Output1 687954| |1|X1|WAS2| Output1 EOF 

Avec le ci-dessus exécuté:

 $ echo "$x1was12" 3459906 

Bien entendu, la redirection d'un heredoc pourrait également être remplacée par une redirection à partir d'un fichier.


Comment cela marche-t-il? Disons-le:

  • Chaque fois IFS='|' read -r count _ nx was _ IFS='|' read -r count _ nx was _ est exécuté, il lit une seule ligne, en le séparant par | s, en mettant la première colonne en count , en supprimant la seconde en l'associant à _ , en lisant le troisième dans n , le quasortingème dans x , le cinquième dans was et le sixième et tout le contenu suivant dans _ . Cette pratique est discutée en détail dans BashFAQ # 1 .
  • count=${count// /} est une extension de paramètre qui élague les espaces du count variables, en remplaçant tous ces espaces par des chaînes vides. Voir aussi BashFAQ # 100 .
  • "${varname,,}" est un autre paramètre d'extension, celui-ci convertissant le contenu d'une variable en minuscules. (Cela nécessite bash 4.0; dans les versions précédentes, considérez "$(tr '[:upper:]' '[:lower:]' <<<"$varname") comme alternative moins efficace).
  • printf -v "$varname" %s "value" est un mécanisme permettant d' affecter indirectement la variable nommée dans la variable varname .

Sinon pour les noms de variables, le tout pourrait être fait avec deux commandes:

 cut -d '|' -f1 file1.dat | tr -d ' ' > output.txt 

Les noms de variables le rendent plus intéressant. Deux méthodes bash suivent, plus une méthode POSIX

  1. Le code bash suivant devrait faire ce que le code exemple de l’OP était censé faire:

     declare $(while IFS='|' read abcdef ; do echo $a 1>&2 ; echo x1${e,,}$c=${a/ /} done < file1.dat 2> output.txt ) 

    Remarques:

    • Le shell bash est nécessaire pour ${e,,} (transforme ” WAS ” en ” was “) et $a/ /} (supprime un espace principal qui pourrait être dans $a ) et declare .
    • La boucle while parsing file1.dat et file1.dat un ensemble d’affectations de variables. Sans declare ce code:

       while IFS='|' read abcdef ; do echo x1${e,,}$c=${a/ /} ; done < file1.dat 

      Les sorties:

       x1was12=3459906 x1was22=334596 x1was11=3495792 x1was21=687954 
    • La boucle while sort vers deux stream distincts: stdout (pour le declare ) et stderr (utilisant les redirections 1>&2 et 2> pour output.txt ).

  2. Utiliser des tableaux associatifs bash :

     declare -A x1was="( $(while IFS='|' read abcdef ; do echo $a 1>&2 ; echo [${e/WAS/}$c]=${a/ /} done < file1.dat 2> output.txt ) )" 

    Dans ce cas, les noms de variable nécessitent des crochets:

     echo ${x1was[21]} 687954 
  3. Code shell POSIX (testé avec dash ):

     eval $(while IFS='|' read abcdef ; do echo $a 1>&2; echo x1$(echo $e | tr '[AZ]' '[az]')$c=$(echo $a) done < file1.dat 2> output.txt ) 
    • eval ne doit pas être utilisé en cas de doute sur ce que file1.dat . Le code ci-dessus suppose que les données de file1.dat sont uniformément fiables.