1

I hope you can help me. I try to separate a String:

#!/bin/bash


file=$(<sample.txt)
echo "$file"

The File itself contains Values like this:

(;FF[4]GM[1]SZ[19]CA[UTF-8]SO[sometext]BC[cn]WC[ja]

What I need is a way to extract the Values between the [ ] and set them as variables, for Example:

$FF=4
$GM=1
$SZ=19
and so on

However, some Files do not contain all Values, so that in some cases there is no FF[*]. In this case the Program should use the Value of "99"

How do I have to do this?

Thank you so much for your help. Greetings Chris

1
  • Do you know any other languages more suitable for such things? Such as Perl, Python, or Ruby? Commented Jun 12, 2014 at 12:29

4 Answers 4

1

It may be a bit overcomplicated, but here it comes another way:

grep -Po '[-a-zA-Z0-9]*' file | awk '!(NR%2) {printf "declare %s=\"%s\";\n", a,$0; next} {a=$0} | bash

By steps

Filter file by printing only the needed blocks:

$ grep -Po '[-a-zA-Z0-9]*' a
FF
4
GM
1
SZ
19
CA
UTF-8
SO
sometext
BC
cn
WC
ja

Reformat so that it specifies the declaration:

$ grep -Po '[-a-zA-Z0-9]*' a | awk '!(NR%2) {printf "declare %s=\"%s\";\n", a,$0; next} {a=$0}' 
declare FF="4";
declare GM="1";
declare SZ="19";
declare CA="UTF-8";
declare SO="sometext";
declare BC="cn";
declare WC="ja";

And finally pipe to bash so that it is executed.

Note 2nd step could be also rewritten as

xargs -n2 | awk '{print "declare"$1"=\""$2"\";"}'
Sign up to request clarification or add additional context in comments.

Comments

1

I'd write this, using ; or [ or ] as awk's field separators

$ line='(;FF[4]GM[1]SZ[19]CA[UTF-8]SO[sometext]BC[cn]WC[ja]'
$ awk -F '[][;]' '{for (i=2; i<NF; i+=2) {printf "%s=\"%s\" ", $i, $(i+1)}; print ""}' <<<"$line"
FF="4" GM="1" SZ="19" CA="UTF-8" SO="sometext" BC="cn" WC="ja" 

Then, to evaluate the output in your current shell:

$ source <(!!)
source <(awk -F '[][;]' '{for (i=2; i<NF; i+=2) {printf "%s=\"%s\" ", $i, $(i+1)}; print ""}' <<<"$line")
$ echo $SO
sometext

To handle the default FF value:

$ source <(awk -F '[][;]' '{
   print "FF=99"
   for (i=2; i<NF; i+=2) printf "%s=\"%s\" ", $i, $(i+1)
   print ""
}' <<< "(;A[1]B[2]")
$ echo $FF
99
$ source <(awk -F '[][;]' '{
   print "FF=99"
   for (i=2; i<NF; i+=2) printf "%s=\"%s\" ", $i, $(i+1)
   print ""
}' <<< "(;A[1]B[2]FF[3]")
$ echo $FF
3

Comments

0

Per your request:

while IFS=\[ read -r A B; do
    [[ -z $B ]] && B=99
    eval "$A=\$B"
done < <(exec grep -oE '[[:alpha:]]+\[[^]]*' sample.txt)

Although using an associative array would be better:

declare -A VALUES

while IFS=\[ read -r A B; do
    [[ -z $B ]] && B=99
    VALUES[$A]=$B
done < <(exec grep -oE '[[:alpha:]]+\[[^]]*' sample.txt)

There you could have access both with keys ("${!VALUES[@]}") and values "${VALUES['FF']}".

2 Comments

Might I suggest replacing eval with declare, eval can do a lot of damage with malicious input, worst declare can do it override an enviromental variable for the sake of the session.
At this point eval is safe. And in case of declaration mistakes I don't see any difference with either form of declarations. declare just creates a silent error but it's still a scripting mistake. What's more is that declare defines values to variables as local to the function by default. -g option has also only been available in 4.2.
0

I would probably do something like this:

set $(sed -e 's/^(;//' sample.txt | tr '[][]' ' ')
while (( $# > 2 ))
do
  varname=${1}
  varvalue=${2}

  # do something to test varname and varvalue to make sure they're sane/safe

  declare "${varname}=${varvalue}"

  shift 2
done

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.