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: (deprecated since Python 2.7, use optparse
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 ofgrep
.
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
Reason of a Directory Size Being Zero
How to Test If a Directory Is Empty with Find
How to Mmap() a Large File Without Risking The Oom Killer
Why Does Munmap Needs a Length as Parameter
Error Hh604: Error Running JSON-Rpc Server: Error:0308010C:Digital Envelope Routines::Unsupported
The Only Overhead Incurred by Fork Is Page Table Duplication and Process Id Creation
Google API to Find The Search Count
Qt Does Not Create Output Files in Debug/Release Folders in Linux
Sqlite Data File on Linux and Os X Incompatible
Linux: How to Send Tcp Packet from Specific Port
Is It Secure to Rely on "X-Forwarded-For" to Restrict Access by Ip in Apache While Using Cloudflare
Proxmox with Opnsense as Firewall/Gw - Routing Issue
Capture Output of a Bash Command, Parse It and Store into Different Bash Variables
Kill Bash Script Foreground Children When a Signal Comes
Sshfs, Linux - How to Mount with Read-Only Access
Get The Count of Bytes Waiting on a Serial Port Before Reading, Linux