-1

I'm trying to use the while read command to create a comma separated table of names, routers, and IP addresses. It should then SSH through that series of routers with the router1 value, input a command, and extract the IP addresses it should return, then write them to a file. It is my understanding that I can do this with read.


 grep -v '^#' /opt/pp1/projectprefixed/peeringinfo > /opt/pp1/projectprefixed/$DATE/peeringinfo.conf

ip_regex='([0-9]{1,3}\.){3}[0-9]{1,3}'

while IFS=, read NREN router1 peering
do

  if [ ! -f /opt/pp1/projectprefixed/$DATE/recFromNRENList/$router1 ] 
    then
    ssh -q -n -o StrictHostKeyChecking=no srv_oc_scripts@$router1 "show route recieve-protocol bgp" | grep -o "$ip_regex" > peeringsReceived
  fi

done < $router1

The table is a text like that looks like this:

NAME1,mx1.ROUTER1,192.168.1.1
NAME2,mx1.ROUTER2,192.168.65.7

However, it returns an "ambiguous redirect" error.

Navar0ne
  • 1
  • 3
  • 2
    1st, don't post screen shots, post the actual code. 2nd, the screen shot you post does not even include the word `done` anywhere, so it is clearly missing some detail. Get rid of the link to the screen shot and include accurate, complete code in the question. – William Pursell Jul 17 '23 at 10:34
  • Quite likely the problem is simply the 'subprocess in a pipe' issue (I don't know that it really has a name, but it is a common error.) When you write `cat path | while read ...`, the body of the while loop is in a subprocess created for the pipe, and any variable assignments made in that subprocess go out of scope. This is also an example of "useless use of cat" (UUOC), which in this case is of primary importance since without the UUOC there is no pipe. – William Pursell Jul 17 '23 at 10:37
  • The code that's posted now looks like it is missing a `fi` to close the final `if` statement. – William Pursell Jul 17 '23 at 10:52
  • 2
    `if [ -s $router1 ]` does not do what you (probably) expect, because `[ -s $router1 ]` will always succeed. If `$router1` is the empty string, that command becomes `[ -s ]`, which will succeed because `-s` is not the empty string. You need to use quotes and write `if [ -s "$router1" ]` so that `[` gets 3 arguments, the second of which is the empty string. (The 3rd argument is `]`) – William Pursell Jul 17 '23 at 10:55
  • The `grep -o` inside the final `if` is also going to read from the pipe, so the `while` loop is only going to perform 1 iteration. Is that intended? – William Pursell Jul 17 '23 at 10:57
  • So if I don't specify the file with cat, how does it know which one to delimitate? – Navar0ne Jul 17 '23 at 10:58
  • Rather than `cat file | while read ...; done`, you usually write `while read ..; done < file`. – William Pursell Jul 17 '23 at 10:58
  • This is not, checking changes now – Navar0ne Jul 17 '23 at 10:59
  • 1
    It looks like the `grep -o` will not actually get any data, though, since the `ssh` is going to consume everything from the pipe. What do you want as the input to `ssh` and `grep`? – William Pursell Jul 17 '23 at 10:59
  • I want to ssh into a router, run the specified command and pull only the matching pattern and write it to a file. It's meant to be an IP address – Navar0ne Jul 17 '23 at 11:17
  • code has been updated – Navar0ne Jul 17 '23 at 11:18
  • I don't know what "opening" a loop is supposed to mean, but does it reach the `if` inside the loop? Turn on tracing (`set -x`) and post the output you get. – user1934428 Jul 17 '23 at 11:56
  • What is `$router1`? – kvantour Jul 17 '23 at 12:10
  • You are reading from the file `$router1` while, at the same time, you are writing to that file. This creates raceconditions and various forms of screams within your system. – kvantour Jul 17 '23 at 12:11
  • `while` is reading from file `$router1` but within the `while` loop `ssh` overwrites file `$router1` and then `grep` overwrites file `router1`; while there's *technically* nothing wrong with this I'm guessing you actually need to be reading from one file and writing (appending?) to another/different file – markp-fuso Jul 17 '23 at 12:16
  • you've modified the code multiple times; please update the description to state your current issue; if you're receiving error messages then also update the question to show the complete error message(s) – markp-fuso Jul 17 '23 at 12:17
  • It's not a `while IFS=,` command it's a `while read` command. Setting IFS just changes how `read` works. – Charles Duffy Jul 17 '23 at 13:16
  • To be clear, the "ambiguous redirect" is because the `<$router1` after the `done` happens before `read` _puts anything into_ `router1`. The variable doesn't have a filename or anything else in it yet, and trying to redirect from an empty string is... ambiguous, as the error states. Obviously you can't read a filename from a file that you don't yet know the name of. – Charles Duffy Jul 17 '23 at 13:38
  • note that my comments above regarding the quotes in `if [ -s $router1 ]` are completely wrong. (Well, not completely; you definitely want to use quotes and write `[ -s "$router1" ]`, but not for the reasons I state.) I was thinking about `[ -z "$router1" ]`, which is a very different animal. But you certainly want to use quotes there, even if the person giving you reasons is giving you the wrong reasons. – William Pursell Jul 18 '23 at 00:09

2 Answers2

1

There are several major logic errors

  1. ssh will consume stdin. Read from a different file descriptor
    while IFS= read -r NREN router1 peering <&3; do
      ...
    done 3< 4router1
    
  2. router1 appears to be initialized in the read command, but is used as an input file to the loop beforehand, and as the ssh remote host, and as a file name you test for existence, and as the output file for the loop commands. I can't tell how this file is supposed to exist, and what contents it's supposed to have
  3. grep will also consume stdin: You probably forgot to specify what input you want for grep

I want to ssh into a router, run the specified command and pull only the matching pattern and write it to a file. It's meant to be an IP address

As a wile stab in the dark, perhaps you want

ip_regex='([0-9]{1,3}\.){3}[0-9]{1,3}'
output_dir="/opt/pp1/projectprefixed/$DATE/recFromNRENList"
router1=???

ip_addr=$(
    ssh -q -n -o StrictHostKeyChecking=no \
        "srv_oc_scripts@$router1" \
        "show route recieve-protocol bgp" \
    | grep -o "$ip_regex"
)
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
0

Redirection is set up before any command execution is done. This implies that the input redirect send to the while-loop is never updated and will always read the initial value of "router1" as input.

In a simplified form of code, here we create two files, read foo in the while-loop and attempt to change the input file of the while-loop mids run.

#!/usr/bin/env bash
echo $'bar\nbaz' > foo
echo $'qux' > bar
f=foo;i=0
while read -r f; do echo $f; f=bar; done < "$f"
grep . *

This does not work because the while loop's input is assigned to the file-pointer connected to foo and you cannot change that.

Where does the error come from? Most likely related to Getting an "ambiguous redirect" error

Most likely, you forgot to assign router1 before the while-loop or the variable router1, which is local to the while-loop, is empty. This is in agreement with the errors mentioned in the linked question.

kvantour
  • 25,269
  • 4
  • 47
  • 72