Why does this cause an infinite loop?

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
  ipcrm -s "$(ipcs | awk '{print $2}')"
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 (""). Oct 9, 2014 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, 2014 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, 2014 at 14:19
  • @Archemar I want to remove the Semaphore. I added more comments.
    – cokedude
    Oct 9, 2014 at 14:29

2 Answers 2


$(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

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
  • how are Semaphore and Semaphore not equal? I would think when they are equal it would exit.
    – cokedude
    Oct 9, 2014 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. Oct 9, 2014 at 14:05
  • 2
    To put it simpler, ipcs output contains Semaphore, but it is not Semaphore only.
    – Isaac
    Oct 9, 2014 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, 2014 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. Oct 9, 2014 at 16:36

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

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

Which produces this output:

not semaphore
not semaphore
not semaphore
not semaphore

not semaphore
is semaphore
not semaphore

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .