1

I am trying to ping all ip's on 192.168.2-254 simultaneously and get the return status of each one. this is what I have so far but it doesn't work and instead just returns the status of xargs. Any help is appreciated! I do not want to use nmap...

subnet="192.168.1"
num="2"
  while [ "$num" -lt "254" ]; do
     num=$((num+1))
     printf "${subnet}.${num}\n"
  done | xargs -n 1 -I ^ -P 50 ping -c2 -t3
  • the `^` is special rather has a meaning to the shell regardless if it is a valid `xargs` syntax/arguments. It needs to be escape/qutoed. – Jetchisel Sep 14 '21 at 00:23
  • Ok, tbh I used part of someone else code (don't judge I know you do it too). And so all I knew was that the xargs part would execute ping simultaneously with a max of 50 processes running at the same time and the ^ was a part of the command. Could you help me rephrase the command to let me get the status of ping (maybe with '$?' ?" –  Sep 14 '21 at 00:29
  • I don't use `xargs` so I'm afraid I can't help. – Jetchisel Sep 14 '21 at 00:34
  • Does this answer your question? [how to get exit code when using xargs (parallel)](https://stackoverflow.com/questions/23055708/how-to-get-exit-code-when-using-xargs-parallel) – kvantour Sep 14 '21 at 02:20
  • @Jetchisel `the ^ is special rather has a meaning to the shell` What meaning does `^` have in shell? `echo ^^^^` just prints `^^^^`. I do not know if `^` is special, could you post some link for me to read? – KamilCuk Sep 14 '21 at 07:48
  • 1
    @KamilCuk, I was thinking the Bourne shell, not Bash my bad, which `^` is same as the pipe in bash/sh. – Jetchisel Sep 14 '21 at 08:26

2 Answers2

1

The well-established and documented behavior of xargs is to return an exit code of its own which indicates what happened to all of the subprocesses you started. To wit;

xargs exits with the following status:

0 - if it succeeds

123 - if any invocation of the command exited with status 1-125

124 - if the command exited with status 255

125 - if the command is killed by a signal

126 - if the command cannot be run

127 - if the command is not found

1 - if some other error occurred.

(Source: GNU xargs man page. The POSIX spec is slightly less specific, and simply lumps all of 1-125 into "A command line meeting the specified requirements could not be assembled, one or more of the invocations of utility returned a non-zero exit status, or some other error occurred.")

There is no simple way to communicate the status of multiple processes in a single exit code (recall that the value is a single byte, so even if you were to encode return values as bit fields, indicating only success or failure, you could still only cram eight of them into one value) but if I understand your request correctly, just run ping in the loop you are running anyway, and wait individually for each one. The following uses a Bash array to keep track of the individual processes:

declare -a procs

for((num=2; num<254; num++)); do
    ping -c2 -t3 "192.168.1.${num}" &
    procs+=($!)
done
for p in "${procs[@]}"; do
    wait $p; echo $?
done

The output from over 200 ping processes running in parallel will be rather noisy; perhaps add >/dev/null after the first done. (Redirecting everything once is more efficient than redirecting each ping separately.)

This doesn't yet keep track of which process ID belonged to which IP address; if you need that, probably use an associative array (Bash 4+) or put the IP addresses into a second array and keep them aligned so that ${ip[x]} is the IP address belonging to process ${procs[x]} (e.g. MacOS still ships with Bash 3.2.57(1)-release which doesn't support associative arrays).

Here's a refactoring to use an associative array.

declare -A procs

for((num=2; num<254; num++)); do
    ping -c2 -t3 "192.168.1.${num}" &
    procs[$num]=$!
done >/dev/null
for p in "${!procs[@]}"; do
    wait ${procs[$p]}
    printf "%s:%i\n" "192.169.1.$p" $?
done
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Ok, several things. Thats really helpful. Second, I'll look at the man page for bash arrays because I have no idea how they work and I usually screw up my code by pasting bits that other people wrote that I need to modify first. And third, I don't need to keep track of the pid but I actually use bash 5.1.8 on Mac OS since I installed it with brew and then symlinked everything. And finally, TYSM guys for helping out. –  Sep 14 '21 at 12:20
  • Added an associative arrays version. – tripleee Sep 14 '21 at 12:30
  • oh I just found out. you know in the first code snippet where it says `for p in`... well in that loop you can printf ${p} and it will print the pid. If it doesn't work for you lemme know. –  Sep 14 '21 at 12:39
  • is there a way I can get the ip that is being processed and print if it worked or failed? I added this `wait $ping && echo "${} is UP" || echo "${} is DOWN"` to the first code snippet you posted but I don't know what variable to use or where I can insert a variable that would have the ip in it. –  Sep 14 '21 at 12:58
  • That's what the associative array version does. (It only prints the exit code but it should be trivial to adapt; you already know how.) – tripleee Sep 14 '21 at 12:59
0

@M Horton Will this serve your purpose . In xargs field use xargs -n1 -P50 ping -c2

A.Ranjan
  • 119
  • 1
  • 2
  • 12
  • Ok so the problem with that is, it pings all the ip's and then it prints the exit code of xargs instead of printing the exit code for ping every time it pings an ip –  Sep 14 '21 at 01:12
  • so I get ```2 packets transmitted, 0 packets received, 100.0% packet loss``` and then 1 after all of that –  Sep 14 '21 at 01:12
  • you can echo out result xargs -n1 -P50 ping -c2 ; echo $? – A.Ranjan Sep 14 '21 at 01:38
  • That still prints the exit status of `xargs`. If you want to return the status of each `ping` separately, you could do `xargs -n 1 -P 50 sh -c 'ping -c 2 "$1"; echo $?' _` but the usefulness of `xargs` is rather limited at this point, the code is moderately complex, and the overhead of running a separate shell for each `ping` is nontrivial. – tripleee Sep 14 '21 at 06:40