2

I am trying to create a script that opens up multiple gnome-terminals and opens a netcat connection. After a couple seconds I would like the terminals to close. I am at a loss and I believe it can't be done, however I figured I'd ask.

This is what I have so far, however after I run it the process id's are considered incorrect and I believe it's because gnome-terminal forks the process to bash and I am not capturing that pid.

ipaddress=$1
port=$2
processarray=()
echo "netcat command set to ip: $ipaddress and port: $port"
if [ -z "$ipaddress" ]; then
    echo "No ip address entered"
fi
if [ -z "$port" ]; then
    echo "No port entered default set to 0"
    $port=0
fi
for i in {0..9}; do
    gnome-terminal -e "bash -c \"netcat $ipaddress $port; exec bash\"" & processarray+=  ("$!")
done

sleep 10;
for i in "${processarray[@]}"; do
    kill "$i"
done
  • 1
    Also, kill can accept multiple arguments. No need for the loop there. – muru Sep 13 '14 at 12:45
1

That's because gnome-terminal is "clever". When you fire up gnome-terminal, it will determine if another instance of gnome-terminal is already running.

  • If no, it creates a new gnome-terminal process and spawns a window.
  • If yes, it will simply cause that existing gnome-terminal process to spawn a new window.

Therefore, all gnome-terminal windows run as a single process, with a single PID.

You can easily check this by opening up several gnome-terminal windows, then get their PIDs by issuing xprop _NET_WM_PID somewhere and click inside them. You will see the PID is the same for all windows.

So what happens in your script?

You're capturing the PIDs of the gnome-terminal instances you launch alright, but the problem is, those PIDs exist only for the fraction of a second. These processes only cause the existing gnome-terminal process to spawn a new window, then terminate.

So if you already have an active gnome-terminal process when executing your script (which I assume), the PIDs you collect don't exist anymore when you try to kill them. If you don't (say, you execute that script from an xterm or some other terminal emulator), the first PID you collect will indeed be "correct". The following PIDs you collect will be "incorrect". Once you enter your killing loop, the first iteration would kill all gnome-terminal windows, and the following ones would output errors, because those PIDs don't exist anymore.

So, how to solve this?

Well, off the bat I can think of two ways.

  1. If you don't insist on using gnome-terminal, use some other terminal emulator instead which doesn't behave in this way. xterm for example spawns a separate process for each window. If you use xterm rather than gnome-terminal in your script, it should work. Easy & clean.

  2. If you do insist on using gnome-terminal, the way to kill a specific gnome-terminal window would be to kill the process that's running inside that specific window. As soon as you terminate that process, that window will close. But since you are using that -e option, this process will be netcat $ipaddress $port first, then afterwards bash, and it is hard to get the PIDs. You can't easily collect them as you are currently doing with $!. You could try something like killall netcat or killall bash, but clearly that will also terminate all other netcat or bash instances, which you probably don't want. You could possibly grep over some cleverly designed ps call to try and find the exact PIDs you want to kill, but that's shaky and error-prone. Honestly, I'd just go for solution 1.

Two minor comments on your script:

  • Line 10: It should be port=0, not $port=0.

  • Line 13: It should be processarray+=("$!"), not processarray+= ("$!").

Hope that helps.

Edit: Oh, I just found the --disable-factory option for gnome-terminal, which causes it to not re-use existing processes. Here's your fixed script:

#!/bin/bash

ipaddress=$1
port=$2
processarray=()
echo "netcat command set to ip: $ipaddress and port: $port"
if [ -z "$ipaddress" ]; then
    echo "No ip address entered"
fi
if [ -z "$port" ]; then
    echo "No port entered default set to 0"
    port=0
fi
for i in {0..9}; do
    gnome-terminal --disable-factory -e "bash -c \"netcat $ipaddress $port; exec bash\"" & processarray+=("$!")
done

sleep 10;
for i in "${processarray[@]}"; do
    kill "$i"
done

  • 1
    Third way: gnome-terminal --disable-factory. Oops, too late. :) – muru Sep 13 '14 at 12:48
  • 1
    Haha, I was 13 seconds faster ;) But thanks. – Malte Skoruppa Sep 13 '14 at 12:49

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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