当前位置: 动力学知识库 > 问答 > 编程问答 >

Content of array in bash is OK when called directly, but lost when called from function

问题描述:

I am trying to use xmllint to search an xml file and store the values I need into an array. Here is what I am doing:

#!/bin/sh

function getProfilePaths {

unset profilePaths

unset profilePathsArr

profilePaths=$(echo 'cat //profiles/profile/@path' | xmllint --shell file.xml | grep '=' | grep -v ">" | cut -f 2 -d "=" | tr -d \")

profilePathsArr+=( $(echo $profilePaths))

return 0

}

In another function I have:

function useProfilePaths {

getProfilePaths

for i in ${profilePathsArr[@]}; do

echo $i

done

return 0

}

useProfilePaths

The behavior of the function changes whether I do the commands manually on the command line VS calling them from different function as part of a wrapper script. When I can my function from a wrapper script, the items in the array are 1, compared to when I do it from the command line, it's 2:

$ echo ${#profilePathsArr[@]}

2

The content of profilePaths looks like this when echoed:

$ echo ${profilePaths}

/Profile/Path/1 /Profile/Path/2

I am not sure what the separator is for an xmllint call.

When I call my function from my wrapper script, the content of the first iteration of the for loop looks like this:

for i in ${profilePathsArr[@]}; do

echo $i

done

the first echo looks like:

/Profile/Path/1

/Profile/Path/2

... and the second echo is empty.

Can anyone help me debug this issue? If I could find out what is the separator used by xmllint, maybe I could parse the items correctly in the array.

FYI, I have already tried the following approach, with the same result:

profilePaths=($(echo 'cat //profiles/profile/@path' | xmllint --shell file.xml | grep '=' | grep -v ">" | cut -f 2 -d "=" | tr -d \"))

网友答案:

Instead of using the --shell switch and many pipes, you should use the proper --xpath switch.

But as far of I know, when you have multiple values, there's no simple way to split the different nodes.

So a solution is to iterate like this :

profilePaths=(
    $(
        for i in {1..100}; do
            xmllint --xpath "//profiles[$i]/profile/@path" file.xml || break
        done
    )
)

or use xmlstarlet:

profilePaths=( $(xmlstarlet sel -t -v "//profiles/profile/@path" file.xml) )

it display output with newlines by default

网友答案:

The problem you're having is related to data encapsulation; specifically, variables defined in a function are local, so you can't access them outside that function unless you define them otherwise.

Depending on the implementation of sh you're using, you may be able get around this by using eval on your variable definition or with a modifier like global for mksh and declare -g for zsh and bash. I know that mksh's implementation definitely works.

网友答案:

Thank you for providing feedback on how I can resolve this problem. After investigating more, I was able to make this work by changing the way I was iterating the content of my 'profilePaths' variable to insert its values into the 'profilePathsArr' array:

# Retrieve the profile paths from file.xml and assign to 'profilePaths'
profilePaths=$(echo 'cat //profiles/profile/@path' | xmllint --shell file.xml | grep '=' | grep -v ">" | cut -f 2 -d "=" | tr -d \")

# Insert them into the array 'profilePathsArr'
IFS=$'\n' read -rd '' -a profilePathsArr <<<"$profilePaths"

For some reason, with all the different function calls from my master script and calls to other scripts, it seemed like the separators were lost along the way. I am unable to find the root cause, but I know that by using "\n" as the IFS and a while loop, it worked like a charm.

If anybody wishes to add more comments on this, you are more than welcome.

分享给朋友:
您可能感兴趣的文章:
随机阅读: