简体   繁体   中英

bash: Find irregular values in strings

I'm looking to fetch a value after a match in a string. Lets say I have two string:

string1="Name: John Doe Age: 28 City: Oklahoma City"
string2="Name: Jane Age: 29 Years City: Boston"

Now I want to set three parameters: Name, Age and City. If I were to do:

name=$(echo "$string1" | awk '{ print $2 $3 }')
city=$(echo "$string1" | awk '{ print $5 }')
city=$(echo "$string1" | awk '{ print $8 $9 }

It would work for string1, but obviously not for string2. After some googling I believe I should put it in some kind of array, but I do not really know how to proceed.

Basically, I want everything after Name: and before Age: to be parameter $name . Everything between Age: and City: to be $age , and so on.

Best regards

Needs bash version 3 or higher:

if [[ $string1 =~ ^Name:\ (.*)\ Age:\ (.*)\ City:\ (.*) ]] ; then
    name=${BASH_REMATCH[1]}
    age=${BASH_REMATCH[2]}
    city=${BASH_REMATCH[3]}
fi

You might need Age:\\ ([0-9]*).*\\ City: if you do not want "Years" to be included in $years .

Perl solution (taken partly from my answer here ):

Capture name:

name=`perl -ne 'print $1 if /Name: ([a-zA-Z ]+) Age:/' <<< $string`

Capture age:

age=`perl -ne 'print $1 if /Age: ([0-9a-zA-Z ]+) City:/' <<< $string`

-ne tells perl to loop the specified one-liner over the input file or standard input without printing anything by default (you could call it awk emulation mode).

The parens in the regexes specify the bits you're interested in capturing. The other fragments acts as delimiters.

After running both of these through $string1 of your example I get 'John Doe' and '28'.

Edit: replaced echo $string with <<< $string , which is nice.

awk is the best solution for this because you can set the field separator to a regex and then your fields are $2, $3 and $4

name=$(awk -F'[[:alpha:]]+: ' '{print $2}' <<<"$string1")
 age=$(awk -F'[[:alpha:]]+: ' '{print $3}' <<<"$string1")
city=$(awk -F'[[:alpha:]]+: ' '{print $4}' <<<"$string1")

Consider these commands:

name=$(awk -F": |Age" '{print $2}' <<< $string1)
age=$(awk -F": |City|Years" '{print $3}' <<< $string1)
city=$(awk -F"City: " '{print $2}' <<< $string1)

Something like this might work:

string1="Name: John Doe Age: 28 City: Oklahoma City"
string1ByRow=$(echo "$string1" | perl -pe 's/(\w+:)/\n$1\n/g' | sed '/^$/d' | sed 's/^ *//')
string1Keys=$(echo "$string1ByRow" | grep ':$' | sed 's/:$//')
string1Vals=$(echo "$string1ByRow" | grep -v ':$')

echo "$string1Keys"
Name
Age
City

echo "$string1Vals"
John Doe 
28 
Oklahoma City

You can use three perl one-liners for assigning value to your variables -

name=$(perl -pe 's/.*(?<=Name: )([A-Za-z ]+)(?=Age).*/\1/' file)

age=$(perl -pe 's/.*(?<=Age: )([A-Za-z0-9 ]+)(?=City).*/\1/' file)

OR

age=$(perl -pe 's/.*(?<=Age: )([0-9 ]+)(?=Years|City).*/\1/' file)

city=$(perl -pe 's/.*(?<=City: )([A-Za-z ]+)"/\1/' file)

Test File:

[jaypal:~/Temp] cat file
string1="Name: John Doe Age: 28 City: Oklahoma City"
string2="Name: Jane Age: 29 Years City: Boston"

Name:

[jaypal:~/Temp] perl -pe 's/.*(?<=Name: )([A-Za-z ]+)(?=Age).*/\1/' file
John Doe 
Jane 

Age:

[jaypal:~/Temp] perl -pe 's/.*(?<=Age: )([A-Za-z0-9 ]+)(?=City).*/\1/' file
28 
29 Years 

OR
if you just want the age and not years then

[jaypal:~/Temp] perl -pe 's/.*(?<=Age: )([0-9 ]+)(?=Years|City).*/\1/' file
28 
29 

City:

[jaypal:~/Temp] perl -pe 's/.*(?<=City: )([A-Za-z ]+)"/\1/' file
Oklahoma City
Boston

I propose a generic solution:

keys=() values=()
for word in $string; do
    wlen=${#word}
    if [[ ${word:wlen-1:wlen} = : ]]; then
        keys+=("${word:0:wlen-1}") values+=("")
    else
        alen=${#values[@]}
        values[alen-1]=${values[alen-1]:+${values[alen-1]} }$word
    fi
done

bash-3.2$ cat sample.log

string1="Name: John Doe Age: 28 City: Oklahoma City"
string2="Name: Jane Age: 29 Years City: Boston"

Using awk match inbuilt function:

awk ' { match($0,/Name:([A-Za-z ]*)Age:/,a); match($0,/Age:([ 0-9]*)/,b);  match($0,/City:([A-Za-z ]*)/,c); print a[1]":" b[1]":"c[1] } ' sample.log

Output:

 John Doe : 28 : Oklahoma City
 Jane : 29 : Boston

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM