Christopher Barry on 30 May 2016 14:30:28 -0700 |
[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]
Re: [PLUG] Bash question |
On Mon, 30 May 2016 12:14:51 -0400 JP Vossen <jp@jpsdomain.org> wrote: >On 05/29/2016 09:22 PM, Christopher Barry wrote: >> On Sun, 29 May 2016 17:26:58 +0000 (UTC) >> Gregory Deal <1deal@comcast.net> wrote: >> >>> Even after checking the "learning bash" book and searching, I'm >>> having a little trouble explaining some bash functioning. I'm >>> trying to process some command output, making an array of its >>> output lines. I was trying to check for the "no output" case (which >>> goes right back to a prompt) by interrogating the final array >>> length. But even with no command output, I get a length of 1. Could >>> someone explain this? Thanks. >>> >>> Script: >>> >>> #!/bin/bash >>> >>> # DTD specifies 0 or 1 <rules>, so check if any before processing. >>> echo "Rules content:" >>> xmlpathval.py ~/program1/sample2.xml "/sync/rules[1]/text()" | >>> while IFS= read -r line; do >>> echo "-->'$line'" >>> done >>> echo "IFS='$IFS'" As a rule, you almost never want to pipe stuff into a while loop. The reasoning is you've created a subshell by doing this, and any vars set in the loop are discarded after the loop (and subshell) terminates. Use a process substitution instead, e.g. while read foo; do echo "${foo}" foo_array+=( "${foo}" ) done < <(generate_foo_lines) # foo_array exists after loop terminates echo "${foo_array[@]}" I realize you're only echo-ing as you read the lines here, but still, the pipe-into structure is a construct you should try to avoid. >>> >>> rulesInfo=$(xmlpathval.py ~/program1/sample2.xml >>> "/sync/rules[1]/text()") <<<<<<< raw output shown below if [[ -z >>> $rulesInfo ]]; then echo "No rules found." >>> fi >>> echo "rulesInfo='$rulesInfo'" >>> >>> IFS= >>> unset rulesArray >>> readarray -t rulesArray <<< "$rulesInfo" >>> echo "length=${#rulesArray[@]}" >>> if [[ ${#rulesArray[@]} -eq 0 ]]; then >>> echo "empty" >>> else >>> echo "not empty" >>> fi >>> echo "rulesArray(single echo)=${rulesArray[@]}" >>> echo rulesArray from loop= >>> for element in "${rulesArray[@]}" >>> do >>> echo "'$element'" >>> done >>> >>> Input: >>> >>> gkd@sisko:~/program1$ xmlpathval.py sample2.xml >>> "/sync/rules[1]/text()" gkd@sisko:~/program1$ echo $? >>> 0 >>> >>> Results : >>> >>> gkd@sisko:~/scripts$ ./test.sh >>> Rules content: >>> IFS=' >>> ' >>> No rules found. >>> rulesInfo='' >>> length=1 >>> not empty >>> rulesArray(single echo)= >>> rulesArray from loop= >>> '' >>> gkd@sisko:~/scripts$ >>> >>> >> >> t=( '' ) >> echo ${#t[@]} >> 1 >> >> yet, >> >> [[ ${t[@]} ]] && echo yes >> <no output> >> >> therefore, >> >> test for an actual value in ${rulesArray[@]} as well >> >> (( ${#rulesArray[0]} )) && [[ "${rulesArray[@]}" ]] && do stuff > > >May be useful: >* http://www.gnu.org/software/bash/manual/html_node/Arrays.html >* http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_10_02.html >* http://www.thegeekstuff.com/2010/06/bash-array-tutorial/ > >As shown in your `echo "length=${#rulesArray[@]}"` you are getting >*something* into that array. I'd try `echo "length=${#rulesArray[@]}" >| hexdump -C` to see what it is, though I suspect a trailing "\n" is >creating an empty element. his 'readarray -t' strips the trailing newline. Pretty sure the data in his array is the null string, as shown in my simple example above using the array ${t[@]}. > >That said, you could probably use something like this to test: > if [ "${#rulesArray[@]}" -eq 0 -a -n "${rulesArray[@]}" ]; then >Or even simpler > if [ -n "${rulesArray[@]}" ]; then In "bash" today, '[' is deprecated, and '[[' is preferred for many, many reasons. '[' is for sh, which is rarely actually needed anymore except on very old systems, or when POSIX compliance is being forced. The fact that he's using readarray says to me that POSIX compliance is not a high priority. repeated from previous post here: (( ${#rulesArray[0]} )) && [[ "${rulesArray[@]}" ]] && do stuff is likely the simplest solution to this problem. (( ${#rulesArray[0]} )) returns true if the var between double-parens evaluates to a non-zero integer value. [[ "${rulesArray[@]}" ]] returns true if the var evaluates to a non-null string value. Notice that no switches inside the (( )) or [[ ]] are needed for this. > >If there is a first element that is not empty...well then you are "not >empty". See `help test` for the meanings of "-a", "-n", etc. (`help` >is "man" for bash internal commands. :) > >I have to admit I find bash arrays very confusing because of all the >"random" punctuation. I want to add a bunch more on that stuff >if/when we ever get around to the 2nd edition of the _bash Cookbook_. > >You might also look at `help read` and the "-a array" option. He's using 'readarray' (in one block of code), the bash builtin designed for this purpose. As an aside, insisting upon sh or POSIX compatibility at all times, and eschewing the great benefits and advances made in recent bash versions is mostly inappropriate today. Unless you absolutely must be compatible with sh for some legacy installation without bash, or the host is running a version of bash earlier than v4.x, continuing to write bash scripts as clunky sh code, like it's still 1995, is missing out on a lot of the power and flexibility that bash has to offer today IMHO. maybe something like this? (obviously not tested with your input...) #!/bin/bash # show rules if they exist declare -a rulesArray=() readarray rulesArray < <( xmlpathval.py \ ~/program1/sample2.xml "/sync/rules[1]/text()" ) (( ${#rulesArray[0]} )) && [[ "${rulesArray[@]}" ]] && { echo -e >&2 "Rules:\n${rulesArray[@]}" } || { echo >&2 "No rules."; false } exit $? I've been hacking bash for two decades and love pushing it's envelope. Some examples of my decidedly non-compliant hackery can be found here: https://github.com/christopher-barry -- Regards, Christopher ___________________________________________________________________________ Philadelphia Linux Users Group -- http://www.phillylinux.org Announcements - http://lists.phillylinux.org/mailman/listinfo/plug-announce General Discussion -- http://lists.phillylinux.org/mailman/listinfo/plug