2

Why does this cause an infinite loop?

#!/bin/bash
while [[ "$(ipcs | awk '{print $2}')" != "Semaphore" ]]; do
  #Gonna get rid of the echo after working
  echo "$(ipcs | awk '{print $2}')"
  #I want to keep this
  ipcrm shm "$(ipcs | awk '{print $2}')"
  #I want this run after breaking out of the loop until I reach the string
  #Message. 
  ipcrm -s "$(ipcs | awk '{print $2}')"
done      
echo
exit 0

I have verified I eventually get Semaphore so it should break out of the while loop.

$ echo $(ipcs | awk '{print $2}')
Shared shmid 262145 294914 2326531 Semaphore semid Message msqid


$ ipcs

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 262145     bob        600        393216     2          dest         
0x00000000 294914     bob        600        393216     2          dest         
0x00000000 2490371    bob        600        998400     2          dest         

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

$ echo $(ipcs |  awk '{print $1}')
------ key 0x00000000 0x00000000 0x00000000 ------ key ------ key

$ echo $(ipcs |  awk '{print $2}')
Shared shmid 262145 294914 2490371 Semaphore semid Message msqid
  • 1
    Instead of "Semaphore", you could use *Semaphore* without the quotes (""). – geedoubleya Oct 9 '14 at 13:55
  • which OS are you using ? From you sample, I suspect Semaphore is the first field, not the second. You sould also use ipcs -s to check only for Semaphore. – Archemar Oct 9 '14 at 13:56
  • You're looking for a match of "...Semaphore..." to "Sempaphore". You need to make the string you're looking for a glob as well. With that check you're essentially saying while [[ 1 ]]. – slm Oct 9 '14 at 14:19
  • @Archemar I want to remove the Semaphore. I added more comments. – cokedude Oct 9 '14 at 14:29
4

$(ipcs | awk '{print $2}') is never equal to Semaphore. It is always equal to:

Shared shmid 262145 294914 2326531 Semaphore semid Message msqid

You probably want something like:

for e in $(ipcs | awk '{print $2}'); do
    [[ "$e" = "Semaphore" ]] && break
    echo $e
done

echo
exit 0

You might also like this awk solution:

ipcs | awk '$2 == "Semaphore" {exit;} $2 != "" {print $2}'

A little explanation:

  • If the second field is Semaphore, exit.
  • Otherwise, if this field isn't empty, print it.

Here are a few alternative solutions (assuming I did understand your needs here) :

# List all shared memory segments keys
ipcs -m | awk 'NR > 3 && $1 != "" {print $1}'

# List all shared memory segments IDs
ipcs -m | awk 'NR > 3 && $2 != "" {print $2}'

For each of these examples, you can iterate over the result:

for e in $(above command); do
    echo $e
done
  • how are Semaphore and Semaphore not equal? I would think when they are equal it would exit. – cokedude Oct 9 '14 at 14:04
  • Your $(...) result is never equal to semaphore. It is always equal to the full line (Shared ...). Since your != is never met, the loop never ends. You don't want to test the whole result every time, you want to iterate over it: this is what the for-loop is designed to do. Here, I iterate over the result, and break the loop when I meet Semaphore, which is what you seemed to be trying to do with your while loop. – John WH Smith Oct 9 '14 at 14:05
  • 2
    To put it simpler, ipcs output contains Semaphore, but it is not Semaphore only. – Isaac Oct 9 '14 at 14:10
  • @JohnWHSmith so bash while loops don't have the ability to read one line at a time? I would think $2 would be equal to Shared, then shmid, then 262145, then 294914, then 2326531, then Semaphore, then semid, then Message msqid. Thats not exactly what I was trying to do. I edited my comment above. I want a command to be executed until I reach the string "Semaphore". Then I want another command to be executed until I reach the string "Message" – cokedude Oct 9 '14 at 14:26
  • Bash while loops do have the ability to read one line at a time, but not the way you coded it. $(some_command) runs that command (to completion) and squashes its output to a single line. See slm's answer for how to do what you want. – G-Man Oct 9 '14 at 16:36
1

Here's a slightly different way to approach your while loop:

$ while read line; do 
    echo "$line"; 
    if [[ "$line" != *Semaphore* ]]; then 
        echo "not semaphore"; 
    else 
        echo "is semaphore"; 
    fi;
  done < <(ipcs | awk '{print $2}')

Which produces this output:

...
not semaphore
814972976
not semaphore
815005745
not semaphore
817070167
not semaphore

not semaphore
Semaphore
is semaphore
semid
not semaphore

Notice when it gets to the string "Semaphore" it properly identifies it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.