How to Push a String to Stdin? Provide Input via Stdin on Startup, Then Read Stdin Input Interactively

How do I push a string to stdin? Provide input via stdin on startup, then read stdin input interactively

This might work:

(echo "something"; cat -) | ./my_program

It creates a sub-shell where the first line of output comes from echo and the rest comes from the standard input to cat, which is the terminal (or the script's standard input, at any rate). I use the - to emphasize that cat is required to read from standard input — it is not simply that I've forgotten to specify "$@" or something after the cat command. Omitting the - doesn't make an operational difference; it might make a comprehensibility difference.

Be aware that the input to my_program is no longer a terminal but a pipe connected to the terminal, which can affect the behaviour of the program. The cat process introduces delays, too.

Expect

If that doesn't do the job, then you probably need to use expect instead. This is a general purpose tool for scripting interactions with other programs, and it uses pseudo-ttys (ptys) to make it appear as if a user at a terminal is communicating with the other program.
As anishsane notes, expect has an interact command that can be used to leave you with the user typing to the program after some fixed preamble.

Beware

Adapting this to your second scenario was not a comfortable experience:

(echo date; cat -) | bash -i

The -i tells Bash it is an interactive shell. The date worked, and I got a prompt after it, but I didn't get any more commands executed. Interrupt got me more prompts; pretty much everything else seemed to be ignored. That may be cat doing too much buffering of its output.

I ended up killing that shell from another terminal window. I had better luck with:

(echo date; cat -) | bash

but there were no prompts from Bash. Be careful; make sure you know how to get out of trouble.

I/O Redirection Stunts

rici also pointed out that in this particular case, you could use:

{ { echo date; echo 'exec 0<&3-';} | bash -i; } 3<&0

That's rather clever because the trailing 3<&0 makes a copy of the original standard input (file descriptor 0) on descriptor 3, and then runs bash -i with its input coming from two echo statements. The first requests the date. The second re-redirects things so that the standard input now comes from file descriptor 3 (that's the 0<&3 part) — which is the original standard input, aka 'the terminal' — and closes file descriptor 3 too (that's the trailing -; it is a Bash extension over POSIX shell I/O redirection).

How can I pipe initial input into process which will then be interactive?

You don't need to write a new tool to forward stdin - one has already been written (cat):

(echo "initial command" && cat) | some_tool

This does have the downside of connecting a pipe to some_tool, not a terminal.

How can I open shell and then execute a command inside it

Unfortunately, passing a startup to command to zsh with -c and keeping it open for interactive use (with -i) doesn't work.

Disclaimer: The following solutions were tested from a regular Command Prompt (cmd.exe), not cmder/conemu, though I'd expect them to work there too.

To try them from PowerShell (v3+), insert --% as the first argument after (right after bash.exe).

Here's a workaround:

c:/Windows/System32/bash.exe -c "zsh -c 'zstyle' && exec zsh -i"

Note that command zstyle is executed in a different, transient zsh instance, so this approach won't work for commands whose purpose is to modify the environment of the interactive shell that stays open.

If that is a requirement, things get more complicated (this solution courtesy of this answer):

c:/Windows/System32/bash.exe -c "{ { echo 'zstyle'; echo 'exec 0<&3-';} | zsh -i; } 3<&0"

Note, however, that both commands being executed will be printed before their output, if any, is shown, preceded by the prompt - as if the commands had been typed interactively.

How to prompt for user input and read command-line arguments

To read user input you can try the cmd module for easily creating a mini-command line interpreter (with help texts and autocompletion) and raw_input (input for Python 3+) for reading a line of text from the user.

text = raw_input("prompt")  # Python 2
text = input("prompt") # Python 3

Command line inputs are in sys.argv. Try this in your script:

import sys
print (sys.argv)

There are two modules for parsing command line options: optparse (deprecated since Python 2.7, use argparse instead) and getopt. If you just want to input files to your script, behold the power of fileinput.

The Python library reference is your friend.

Confused about stdin, stdout and stderr?

Standard input - this is the file handle that your process reads to get information from you.

Standard output - your process writes conventional output to this file handle.

Standard error - your process writes diagnostic output to this file handle.

That's about as dumbed-down as I can make it :-)

Of course, that's mostly by convention. There's nothing stopping you from writing your diagnostic information to standard output if you wish. You can even close the three file handles totally and open your own files for I/O.

When your process starts, it should already have these handles open and it can just read from and/or write to them.

By default, they're probably connected to your terminal device (e.g., /dev/tty) but shells will allow you to set up connections between these handles and specific files and/or devices (or even pipelines to other processes) before your process starts (some of the manipulations possible are rather clever).

An example being:

my_prog <inputfile 2>errorfile | grep XYZ

which will:

  • create a process for my_prog.
  • open inputfile as your standard input (file handle 0).
  • open errorfile as your standard error (file handle 2).
  • create another process for grep.
  • attach the standard output of my_prog to the standard input of grep.

Re your comment:

When I open these files in /dev folder, how come I never get to see the output of a process running?

It's because they're not normal files. While UNIX presents everything as a file in a file system somewhere, that doesn't make it so at the lowest levels. Most files in the /dev hierarchy are either character or block devices, effectively a device driver. They don't have a size but they do have a major and minor device number.

When you open them, you're connected to the device driver rather than a physical file, and the device driver is smart enough to know that separate processes should be handled separately.

The same is true for the Linux /proc filesystem. Those aren't real files, just tightly controlled gateways to kernel information.

automatic docker login within a bash script

Docker 18 and beyond

There's now an officially-documented way to do this:

cat ~/my_password.txt | docker login --username foo --password-stdin

Docker 1.11 through Docker 17

You can pass all the arguments on the command-line:

docker login --username=$DOCKER_USER --password=$DOCKER_PASS $DOCKER_HOST

If you don't specify DOCKER_HOST, you'll get the main Docker repo. If you leave out any of the arguments, you'll be prompted for that argument.

Older than 1.11

The same path as just above, except that you need to also pass an --email flag. The contents of this are not actually checked, so anything is fine:

docker login --username=$DOCKER_USER --password=$DOCKER_PASS $DOCKER_HOST --email whale@docker.com

Pipe text to Python script or prompt

You can use sys.stdin.isatty to check if the script is being run interactively. Example:

if sys.stdin.isatty():
message = raw_input('Enter your message ')
else:
message = sys.stdin.read()

Automating telnet session using Bash scripts

Write an expect script.

Here is an example:

#!/usr/bin/expect

#If it all goes pear shaped the script will timeout after 20 seconds.
set timeout 20
#First argument is assigned to the variable name
set name [lindex $argv 0]
#Second argument is assigned to the variable user
set user [lindex $argv 1]
#Third argument is assigned to the variable password
set password [lindex $argv 2]
#This spawns the telnet program and connects it to the variable name
spawn telnet $name
#The script expects login
expect "login:"
#The script sends the user variable
send "$user "
#The script expects Password
expect "Password:"
#The script sends the password variable
send "$password "
#This hands control of the keyboard over to you (Nice expect feature!)
interact

To run:

./myscript.expect name user password


Related Topics



Leave a reply



Submit