Array Length Reports as Having Length when it is Empty?


 
Thread Tools Search this Thread
Top Forums Shell Programming and Scripting Array Length Reports as Having Length when it is Empty?
# 1  
Old 09-13-2012
Array Length Reports as Having Length when it is Empty?

Hello All,

I have this script that does stuff like "starting, stopping & restarting" a Daemon Process running on my machine...

My main question is why in part of my code (which you will see below) does the Array Length (i.e. ${#PIDS[@]} )
return "1" when I know the Array is empty..?

Here is a little explaination of the problem...
Occasionally when runnning the "ps auxww" command the output finds more than one instance of the Daemon. Because of
this I save the PID(s) into an array (which is declared at the start of the script), then inside a function I
save the PID(s) into that array. Like this below...

For Example:
Code:
### Declare the Array for the PID(s)
declare -a PIDS

    :
    :
.... Other Code ....
    :
    :

### START: Function check_ps()...
#####################################
check_ps()
{
    ### Reset ${PIDS[]} to nothing...
    PIDS=""

    ### Save the Output into an array:
    PS_OUTPUT=( $(ps auxww | grep -v grep | grep "/usr/local/bin/Daemon_Process") )

    ### If this array is NOT empty, then start saving the PIDs
    if [ ! -z $PS_OUTPUT ]
     then
        x=0
        for line in "${PS_OUTPUT[@]}"
         do
            PIDS[$x]=$(echo "$line" | awk -F ' ' {'print $2'})
            x=$(($x+1))
        done
    else
        echo "The Daemon is NOT Running!"
    fi
}    
#####################################
### END: Function check_ps()...

Now if I pass the "stop" argument to the script, after I collect all the PIDs using the code above, I then try to
kill the PIDs. Like this...

Code:
stop_daemon()
{
    # Call Function check_ps to get the PIDs
    check_ps

    ### If the Array PIDS contains data, then...
    if [ ${#PIDS[@]} -gt 0 ]
     then
        ### While The Array still contains data, do...
        while [ ${#PIDS[@]} -ne 0 ]
         do
            ### PRINT SOME STUFF FOR TESTING...
            echo "LENGTH = ${#PIDS[@]}"
            echo -e "ELEMENT 0 = \"${PIDS[0]}\"\n"

            kill ${PIDS[0]}
            KILL_RETCODE=$?

            ### Re-Check 'ps auxww' for the daemon
            check_ps
        done
    fi
}

Ok so, I was testing in the while loop in the "stop_daemon()" Function above. Lets say that there was only one PID found.
I had issued some echo commands to see what the values of the variables were since it seemed that it was an endless
loop. I echoed out the Length of the array (i.e. ${#PIDS[@]}) and it ONLY prints "1" everytime, and I also print the
first element of the array (i.e. ${PIDS[0]} ) which prints nothing AFTER the first iteration through the loop.

Example Output:
Code:
LENGTH = 1
ELEMENT 0 = "25589"

LENGTH = 1
ELEMENT 0 = ""

LENGTH = 1
ELEMENT 0 = ""

LENGTH = 1
ELEMENT 0 = ""

LENGTH = 1
ELEMENT 0 = ""
^C            --> HAVE TO KILL THE SCRIPT TO STOP THE LOOP.

Does anyone know why this loop never terminates? I know some of you will say that I don't need an array since I'm re-checking
the 'ps auxww' output on each loop, but I needed the array in another section of my script. Even still I feel like the loop
should be terminating since it has no elements... I think?

Any thoughts would be great!

Thanks in Advance,
Matt
# 2  
Old 09-13-2012
You're not even using the array, though. You're just shoving it into a list with @. Your code is so much more complicated than it'd be without, especially since it's possible -- as you've discovered -- to have blank elements in an array.

It's silly to run awk 3,000 times to process 3,000 lines. awk can process them all at once, and do the grep and grep -v grep too. It's not a glorified cut.

You don't need to use grep -v grep to exclude itself. grep '[p]rocessname' for example won't match '[p]rocessname' but will match 'processname'. The awk expression below won't match itself because of the need to use \/ for /.

Given a list like "PID1 PID2 PID3 PID4" you can loop it like for X in $PIDS # Not quoted! no arrays required. It will do what you expect, and not give you surprises like your array did.

If you need the ability to access elements, you can do set -- $PIDS to get a nice $1 $2 ... $N list, ${!N} to access element N in bash, and even get to use shift to pop off the front element one at a time this way.

You can get a nice space-separated list of PIDs in one operation:
Code:
check_ps()
{
        PIDS="$(ps auxww | awk -v ORS=" " '/\/usr\/local\/bin\/Daemon_Process/ { print $2 } END { printf("\n") }')"

        # Get count of strings in $# by setting $1, $2, ...
        # You weren't using function parameters anyway, so no harm.
        set -- $PIDS

        # Return true when PIDs exist to make your loop simpler
        [ "$#" -gt 0 ] && return 0
        # Return false when there's no PIDs
        return 1
}

while check_ps
do
        for X in $PIDS
        do
                kill $X
                break # Kill only the first by leaving the loop
        done
done


Last edited by Corona688; 09-13-2012 at 01:47 PM..
This User Gave Thanks to Corona688 For This Post:
# 3  
Old 09-13-2012
Hey Corona688, thanks for the reply.

Yea, sorry you have to bear with me I'm no expert at this as you can see...

My problem with coding has always been OVER-Complicating things, so thanks for the suggestions! I have seen the
"grep [p]rocessname" before but hadn't a clue what it was for... Cool thanks!

Also, I need to store all the output from the "ps auxww" command because I'm also getting the start time, cpu, as well as some other stuff
that I didn't include in my code above for the sake of over-complicating my original question... Which I've seemed to do anyway, ha!

And for the moment learning the whole awk language isn't really an option at the moment due to time constraints. But I
appreciate the info and hopefully I can dive into that later...



As for arrays, I thought I remember reading that putting a command in "ARRAY=( $(...) )" would automatically split the output on the IFS and
save it into an array..? I'm guessing that's incorrect?

Just so I'm understanding arrays correctly, would I have to be explicit in giving it an index in order for it to be considered an array?
Like this: not using this but just an example...
Code:
OUTPUT=$(ps auxww | grep [p]rocessname)

IFS='
'

x=0
for line in $OUTPUT
 do
        PID[$x]=$(echo "$line" | awk -F ' ' {'print $2'})
        x=$(($x+1))
done

Anyway thanks for your suggestion, much appreciated!


Thanks Again,
Matt
# 4  
Old 09-13-2012
You're making the same mistakes again. This:
Code:
for X in 9000 lines of junk
do
        echo "single line" | awk '{ extract something from single line }'
done

is almost always wrong since you can do it all with awk '{ extract something } ' file-with-9000-lines-of-junk in one operation instead of 9,000.

If you must save all its output, then:

Code:
OUTPUT="$(ps auxww | grep [p]rocessname)"

# Don't run awk 9,000 times to process 9,000 lines.  Run it ONCE for everything.
PIDS="$(echo "$OUTPUT" | awk '{ print $2 }')"

# Get count in one operation
set -- $PIDS
echo "There are $# PID's"

for X in $PIDS
do
        echo "$X"
done

Note that set -- overwrites your $1 $2 ... commandline variables.

Also note that functions have their own, independent set of $1 $2 ... variables. set -- there does not overwrite the global ones.

Code:
As for arrays, I thought I remember reading that putting a command in "ARRAY=( $(...) )" would automatically split the output on the IFS and
save it into an array..? I'm guessing that's incorrect?

It splits it, yes. But as you've discovered it's possible to get blank elements. And usually there's no point in doing so -- string splitting works everywhere, not just in arrays, so why not cut out the middleman and do it directly?

Last edited by Corona688; 09-13-2012 at 02:16 PM..
# 5  
Old 09-13-2012
Quote:
Also, I need to store all the output from the "ps auxww" command because I'm also getting the start time, cpu, as well as some other stuff
that I didn't include in my code above for the sake of over-complicating my original question... Which I've seemed to do anyway, ha!
How about you explain exactly what you want to do, instead of the way you're trying to do it? There might be a simpler way you're missing.
# 6  
Old 09-13-2012
Quote:
Originally Posted by mrm5102
Just so I'm understanding arrays correctly, would I have to be explicit in giving it an index in order for it to be considered an array?
Like this: not using this but just an example...

Code:
OUTPUT=$(ps auxww | grep [p]rocessname)

IFS='
'

x=0
for line in $OUTPUT
 do
        PID[$x]=$(echo "$line" | awk -F ' ' {'print $2'})
        x=$(($x+1))
done

Anyway thanks for your suggestion, much appreciated!


Thanks Again,
Matt
Like I said, I'm NOT using this code, I was simply asking if that's how you assign into array elements...!


Quote:
is almost always wrong since you can do it all with awk '{ extract something } ' file-with-9000-lines-of-junk in one operation instead of 9,000.
I get what your saying, and that would be better if I was processing a file with 9,000 lines. BUT in this case I'm only checking "ps auxww" which wouldn't
have more then a TOTAL of a couple hundred processes running and I have never seen more then 3-4 instances of the Daemon that I'm looking for, so "9000 times"
is kind of silly to say...

I get it's MUCH more efficient, but after just trying both ways side-by-side, the difference in speed and cpu is unnoticeable...

Anyway, I appreciate your help and explanations with all this but now I think I'm going to go back and rewrite this whole thing... And try and spend a little time with awk.


So thanks AGAIN for your help!

Thanks Again,
Matt
# 7  
Old 09-13-2012
Quote:
Originally Posted by mrm5102
I get it's MUCH more efficient, but after just trying both ways side-by-side, the difference in speed and cpu is unnoticeable...
Ever wonder why flash ads are so good at making computers lag and freeze? They can't have been that bad on the author's computer. And certainly, running by themselves on a studio-grade machine, a flash ad can chug along just fine even jammed on 100% quality maximum framerate. It's when you start putting 3 of them on one page that they start misbehaving and competing with each other. But they work fine for the author, and that's good enough for them!

If your program's the only thing ever running on that computer, it will be okay. But that's not a good habit to learn; someday you won't be able to get away with it. And it makes life so difficult for you anyway...
Login or Register to Ask a Question

Previous Thread | Next Thread

9 More Discussions You Might Find Interesting

1. Shell Programming and Scripting

Convert variable length record to fixed length

Hi Team, I have an issue to split the file which is having special chracter(German Char) using awk command. I have a different length records in a file. I am separating the files based on the length using awk command. The command is working fine if the record is not having any... (7 Replies)
Discussion started by: Anthuvan
7 Replies

2. Shell Programming and Scripting

Flat file-make field length equal to header length

Hello Everyone, I am stuck with one issue while working on abstract flat file which i have to use as input and load data to table. Input Data- ------ ------------------------ ---- ----------------- WFI001 Xxxxxx Control Work Item A Number of Records ------ ------------------------... (5 Replies)
Discussion started by: sonali.s.more
5 Replies

3. Shell Programming and Scripting

Generate 100 Character Fixed Length Empty File

Hello Everyone, I'm running AIX 5.3 and need to generate a 100 character fixed length empty file from within a bash script that I am developing. I searched and was able to find: dd if=/dev/zero of=/test/path/file count=100 however my understanding is that this will generate a file of a... (10 Replies)
Discussion started by: jvt
10 Replies

4. Shell Programming and Scripting

changing a variable length text to a fixed length

Hi, Can anyone help with a effective solution ? I need to change a variable length text field (between 1 - 18 characters) to a fixed length text of 18 characters with the unused portion, at the end, filled with spaces. The text field is actually field 10 of a .csv file however I could cut... (7 Replies)
Discussion started by: dc18
7 Replies

5. Shell Programming and Scripting

Make variable length record a fixed length

Very, very new to unix scripting and have a unique situation. I have a file of records that contain 3 records types: (H)eader Records (D)etail Records (T)railer Records The Detail records are 82 bytes in length which is perfect. The Header and Trailer records sometimes are 82 bytes in... (3 Replies)
Discussion started by: jclanc8
3 Replies

6. UNIX for Dummies Questions & Answers

What the command to find out the record length of a fixed length file?

I want to find out the record length of a fixed length file? I forgot the command. Any body know? (9 Replies)
Discussion started by: tranq01
9 Replies

7. UNIX for Dummies Questions & Answers

Length of a Unix filepath max length

Hi Guys, Could anyone shed some light on the length of a Unix filepath max length pls ? thanks ! Wilson (3 Replies)
Discussion started by: wilsontan
3 Replies

8. UNIX for Dummies Questions & Answers

Sed working on lines of small length and not large length

Hi , I have a peculiar case, where my sed command is working on a file which contains lines of small length. sed "s/XYZ:1/XYZ:3/g" abc.txt > xyz.txt when abc.txt contains lines of small length(currently around 80 chars) , this sed command is working fine. when abc.txt contains lines of... (3 Replies)
Discussion started by: thanuman
3 Replies

9. Shell Programming and Scripting

creating a fixed length output from a variable length input

Is there a command that sets a variable length? I have a input of a variable length field but my output for that field needs to be set to 32 char. Is there such a command? I am on a sun box running ksh Thanks (2 Replies)
Discussion started by: r1500
2 Replies
Login or Register to Ask a Question