1

I am looking for a way to have two stdins to a bash script, namely one that is interactive and another which could leverage redirection

Say I need to invoke a program which asks for credentials of the type username, password I want to feed that interactively and then I want to feed a while loop from a redirection. Something like:

$> ./myscript.sh < input.lst

or

$> cat input.lst | ./myscript.sh

In my script there would be something like (very simplified):

#!/bin/bash
./authentication_application
while read i
do 
  echo $i
done 

Where authentication_application is a binary I don't own (have no access to source code and haven't compiled it myself), I can't change and can't be replaced with any alternative authentication method.

And the invocation would result in

Username: some_user
Password: password 
... contents of input.lst ...

With some_user and password input interactively and contents of input.lst taken from the redirection

I tried redirecting stdin to a new fd and using read -u <fd> inside the script with fd being "7" for instance, and something like this:

#!/bin/bash
exec <fd><&0
exec 0<&-

... but here is where I have no idea of how to re-open (or reset) stdin to the interactive one

Any Ideas? you people even think it's possible?

BTW, I'm not looking for the following:

  • prepending input.lst with username and password on cleartext (nor any kind of cypher hack)
  • cat-ing two files into the script (the first one storing username and password on cleartext)
  • A workaround where I have to use parameters to get_credentials.sh to pass username and password without prompting the user
  • passing the file input.lst as a parameter to the script and using positional parameters to access the file in the while loop (it would defeat the purpose of redirecting the input and leveraging the pipelining nature of the script i.e. $> cat input.lst | ./filter_1.sh | ./filter_2.sh |... | ./filter_n.sh )
Jorge Q
  • 11
  • maybe use some way to time the input ... no input within 250 ms would switch to interactive mode – jsotola Feb 02 '21 at 18:08
  • @jsotola Might be a good idea, but it is the other way around I would have to keep interactive stdin open for "x" seconds and then switch to the redirection. Still would need a way to decouple or split stdin, since new stdin becomes the input file the moment you invoke the redirection. – Jorge Q Feb 02 '21 at 18:12
  • i don't have access to a linux computer right now to test my ideas, so I'm just voicing my thoughts ... couple other ideas to try .... 1) expect piped input at start and switch to interactive mode if nothing comes in .... 2) look at the command line that launched the script ... 3) do not allow piped input, but pass the name input.lst as an argument – jsotola Feb 02 '21 at 18:28
  • Does ./authentication_application ask for the password in stdin? And not e.g. open /dev/tty for that purpose, like ssh does (the client)? I suppose it doesn't have any other method for passing the credentials, like giving it a filename or using an environment variable, because in that case you probably wouldn't be asking this. – ilkkachu Feb 02 '21 at 18:49
  • @ilkkachu, not sure about the difference in those two approaches, but as I commented to @NickD and answered separately, yes the redirection from /dev/tty seems to be the way to go. There are other methods for passing the credentials (the most straight forward is using parameters to ./authentication_applicationn and feeding those in clear text but it would defeat the purpose. The purpose is creating succesive filters in bash that behave like native linux commands but with a small tweak (namely they need authentication) – Jorge Q Feb 02 '21 at 19:02
  • @JorgeQ, yep, if < /dev/tty works, then it indeed reads from stdin. You're right, passing a password on the command line is quite iffy, but through an env var should be ok. E.g. sshpass accepts both, and is able to read from a file. – ilkkachu Feb 02 '21 at 19:04

2 Answers2

1

Something like this perhaps:

#! /bin/bash

echo -n "Password: " read pwd < /dev/tty echo

while read x ;do echo $x done

echo "Password was: " $pwd

You invoke it with ./foo.sh < infile; it then prints the prompt and reads a password from the terminal, but processes the loop from its stdin which is redirected to the file.

I'm sure there are gotchas but it is probably good enough for most cases.

NickD
  • 2,926
  • thanks for the quick reply. Unfortunately no, the authentication utility is neither a bash script nor is it mine to modify it.

    In any case you may have pointed me in the right direction I will try both the following:

    • Rederecting input of auth_utility from /dev/tty
    ./auth_utility < /dev/tty
    
    • Reseting the 0 fd from /dev/tty:
    0<&/dev/tty
    

    And let you know the results. Thanks again

    – Jorge Q Feb 02 '21 at 17:59
  • Then you should clarify your question: your example is exactly the template that I followed. – NickD Feb 02 '21 at 18:24
  • Will do Nick, even though I never specified that it uses "read" to get the credentials, I understand that it can be misleading to give the example using "something.sh".

    In any case I tried variations of your suggestion and so far it is working, so I will upvote it and then post a full working example myself based on your input

    Thank you very much

    – Jorge Q Feb 02 '21 at 18:34
-1

NickD's answer gave me the insight to tackle the issue.

This is the working code for the exact specification

#!/bin/bash
./authentication_application < /dev/tty

while read i do echo $i done

Hoping someone finds it useful

Jorge Q
  • 11
  • This doesn't seem to be all that different from the NickD's answer. You added an unnecessary fd and then removed it, and just change the command reading from the TTY. – muru Feb 02 '21 at 18:56
  • You may want to use ./authentication_application < /dev/tty > /dev/tty 2>&1 or something like that to get any username/password prompts and error messages also on the terminal, in case the output to your script is also redirected. Also, I suppose the while read; do echo is a placeholder, but in case you're actually doing something like that, do note it's slow, and has other issues. You should be able to just run cat, sed or awk there, even within the script. – ilkkachu Feb 02 '21 at 19:07
  • @muru: just added it for simplicity's sake, If you really think it's adds no value to someone else with the same issue I can try to delete it, I do thank NickD both in his reply and my own. You're absolutely ritght about the first working example, complete overkill @ilkkachu, yep the while looop with a do echo is a placeholder for simplicity, even though the loop is in the code doing something a bit more interesting. I will look at the article and evaluate if there is a better way to do what I am trying to. Thanks – Jorge Q Feb 02 '21 at 19:29
  • @JorgeQ we don't do "thank you" posts - if you want to say "thank you", upvote and accept the answer. Even the comment is superfluous. Otherwise I don't think this post adds anything at all. – muru Feb 02 '21 at 19:46