ipython: Can I provide input to a shell command - shell

Can I execute a shell command that requires input in ipython and/or an ipython notebook?
When I execute such a command, I see it's prompt, but no apparent way to provide it with input from my keyboard.
An example could be an rsync command to a remote server (thus requiring a password). There are no doubt dangers security-wise here - these are somewhat reduced in my case as I'm running on localhost.

Reposting as an answer, with a bit more detail:
No, it's a long standing issue that's really difficult to resolve: github.com/ipython/ipython/issues/514
The issue is roughly that our architecture can't tell when a process is waiting for input, so it doesn't know to prompt the user for input.
You may be able to use pexpect and raw_input to simulate this for specific programs. I.e. if you say what the prompt looks like, it spots that in the output, and asks the user for input to send back to the process. E.g. for Python:
import pexpect
p = pexpect.spawn('python')
while True:
try:
p.expect('\n>>> ')
print(p.before)
p.sendline(raw_input('>>> '))
except pexpect.EOF:
break
I've just given this a brief test - it works, but it's fairly rough. The concept could be improved.

Sadly, no.
I couldn't find documentation on this, so I went source-diving. The ipython code that actually performs that transformation is https://github.com/ipython/ipython/blob/master/IPython/core/inputtransformer.py#L208 , specifically:
def _tr_system(line_info):
"Translate lines escaped with: !"
cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
which, in other words, invokes the underlying shell with everything following the !.
ipython is probably not what you want -- check out this answer for a Python alternate to include in scripts.

Was just looking for this and my wee face dropped when I saw it was a bit of an issue. Thought I would just post my solution in case it is usefull to anyone else.
Basically I was looking for a way to send sudo commands through the notebook, probably not very wise but I needed it for what I was doing. And i couldn't get a prompt for the password. So decided to use a x-terminal and sending the command through to the terminal. You don't get any feed back but may be due to not hooking to the IO on the way back. Here is what i used in the notebook:
In [1] !xterm -e sudo mount -o loop system.img /system/
I'm using linux but i would expect !cmd for windows might do the trick too

Many programs that require a password provide a variety of ways to prompt the user for it so that jupyter doesn't have to do it for you. The sudo command has a -A option and the SUDO_ASKPASS environmental variable. I've used it like this to use sudo to get permissions for the /var/log/btmp.1 file:
In [1]: !SUDO_ASKPASS=/usr/bin/ssh-askpass sudo -A lastb -a -f /var/log/btmp.1 | head
Similarly, for your case, ssh has an SSH_ASKPASS environmental variable.
Note that for headless operation of ssh/rsync, you can avoid authentication prompts from a notebook entirely by directly setting up an ssh agent (if it isn't running already) and referring to it with your SSH_AUTH_SOCK variable, like ssh-add does.
Or you can use a different program via SSH_ASKPASS which handles authentication in a custom way. See questions like How to automate SSH login with password? - Server Fault for more details, but be careful to not compromise your security especially when trying to automate connections from one server to another.

Related

How to correctly emulate terminal in linux/macOS using exex(go)?

I need to emulate a terminal in go. I try to do it like this:
lsCmd := exec.Command("bash", "-c", "ls")
lsOut, err := lsCmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(lsOut))
And it seems to work correctly (the native ubuntu terminal displays a horizontal list, and the result of this function goes vertically).
But if I specifically call the wrong command, for example exec.Command ("bash", "-c", "lss"), I get:
panic: exit status 127
And in the native ubuntu terminal I get the following result:
Command 'lss' not found, did you mean:
and enumeration of commands.
I need to communicate with the native terminal, and get the same thing as the result of the command if I wrote the command in the standard ubuntu terminal.
What is the best way to do this? Maybe the exec library is not suitable for this? All this is necessary for front-end communication with the OS terminal. On a simple html/css/js page, the user enters a command, after go it sends it to the native terminal of the operating system and returns the result to the front-end.
How I can get the same result of executing commands as if I were working in a native terminal?
The problem
But if I specifically call the wrong command, for example exec.Command
("bash", "-c", "lss"), I get:
panic: exit status 127
And in the native ubuntu terminal I get the following result:
Command 'lss' not found, did you mean:
and enumeration of commands.
This has nothing to do with Go, and the problem is actually two-fold:
Ubuntu ships with a special package, command-not-found, which is usually preinstalled, which tries make terminal more friently for mere mortals by employing two techniques:
It tries to suggest corrections for misspellings (your case).
It tries to suggest packages to install when the user tries to execute a program which would have been be available if the user had a specific package installed.
When the command is not found, "plain" (see below) shell fails the attempt by returning a non-zero exit code.
This is absolutely expected and normal.
I mean, panicking on it is absolutely unwise.
There's a historical difference on how a shell is run on a Unix system.
When a user logs into the system (remember that back in the days the concept of the shell was invented you'd be logging in via a hardware computer terminal which was basically what your GNOME Terminal window is but in hardware, and connected over a wire),
the so-called login shell is started.
The primary idea of a logic shell is to provide interactive environment for the user.
But as you surely know, shells are also capable of executing scripts.
When a shell executes a script, it's running in a non-interactive mode.
The modes a Unix shell can work in
Now let's dig deeper into that thing about interactive vs non-interactive shells.
In the interactive mode:
The shell is usually connected to a real terminal (either hadrware or a terminal emulator; your GNOME Terminal window is a terminal emulator).
"Connected" means that the shell's standard I/O streams are connected to the terminal, so that what the shell prints is displayed by the terminal.
It enables certain bells and whistles for the user, usually providind limited means for editing what is being input (bash, for instance, engages GNU readline.
In the non-interactive mode:
The shell's standard I/O streams are connected to some files (or to "nowhere" — like /dev/null).
No bells and whistles are enabled — as there is nobody to make use of them.
GNU bash is able to run in both modes, and which mode it runs in depends
on how it was invoked.
When initializing in different modes, bash reads different initialization scripts, and this explains why the machinery provided by the command-not-found package gets engaged in the interactive mode and does not when bash is run otherwise — like in your invocation from Go.
What do do about the problem
The simplest thing to try is to run bash with the --login command-line option or otherwise make it think it runs as an interactive shell.
This might solve the problem for your case but not necessarily.
The next possible problem is that some programs do really check whether they're running at a terminal — usually these are programs which insist on real interaction with the user, usually for security purposes, and there are programs which simply cannot run when not connected to a real terminal — these are "full-screen" text UI programs such as GNU Midnight Commander, Vim, Emacs, GNU Nano and anything like this.
To solve this problem, the only solution is to run the shell in a pseudo-terminal environment, and that's what #eudore hinted at in their comment.
The github.com/creack/pty might be a package to start looking at; golang.org/x/crypto/ssh also provides some means to wrangle PTYs.

Running command with /dev/null on Windows

OK, this question has been asked before (e.g. Is there a /dev/null on Windows?) but I'm trying to find an answer that works, and all of the prior questions just say "change the command to point to NUL"
If I have a curl request (or whatever) which someone ran on a Unix/Mac which includes this:
-o /dev/null
it will throw an error if I try to run it as-is on my Windows box. Therefore, I need to change the command by replacing that with:
-o NUL
My question is, is there something I can do so that I can run the original curl request without needing to make that change?
IOW, can I create a symlink or something similar, so that I don't need to change the curl statement? Basically so I can use the *nix syntax on a Windows box?
Before someone says "how much hassle is it to change the curl", I'm running hundreds of curls a day, often ones which were originally run on a *nix box. Also, if I change to use Windows syntax, then when someone tries to run it on a *nix box, they get issues....

dmenu top bar in xmonad runs some items (Chromium), but not ranger or others

I have a "stock" xmonad install on Arch.
No changes to my xmonad.hs yet
I have installed dmenu.
It runs by alt-p, the default, and displays and filters as expected.
Chromium runs, but other items, like ranger, alsamixer or other tasks do not.
I am not finding anything anywhere about anyone having to do anything to get these items to run, nor anyone having any issues with doing so.
Surely, then, there is something wrong in my install.
my dmenu_run is as follows:
#!/bin/sh
dmenu_path | dmenu "$#" | ${SHELL:-"/bin/sh"} &
I would normally run terminology with bash or zsh. I have tried to alter the SHELL to /bin/bash, but to no avail.
Is there any other place I must look or items I should alter?
Such a shame as I am really liking xmonad so far, and want to get dmenu working before I start exploring xmonad.hs...
Thanks in advance
UPDATE: I have found the following
here over at Archwiki that involves changing dmenu_run and adding a .demenu_term in one's home. It seems to work, but still wonder if there was a more orthadox mechanism.
ranger and alsamixer are applications which run inside a terminal. Imagine (or try) to run ls via dmenu, where should the directory listing be printed to without a terminal?
You look for functionality which is provided either by prompt imported from XMonad.Prompt.Shell by using a convinient keybinding like
((modm .|. shiftMask, xK_c), prompt ("xterm" ++ " -e") greenXPConfig)
(described in the linked documentation) or shellPrompt where you execute
xterm -e alsamixer
or any other command, e.g.
feh path/to/image/you/want/to/open/now.jpg
instead of opening a terminal, running above with tailing & and exiting the terminal.

In Bash, how can I tell if I am currently in a terminal

I want to create my own personal logfile that logs not only when I log in and out, but also when I lock/unlock my screen. Kindof like /var/log/wtmp on steroids.
To do this, I decided to run a script when I log into Ubuntu that runs in the background until I quit. My plan to do this is to add the script to .bashrc, using ./startlogging.sh & and in the script I will use trap to catch signals. That's great, except .bashrc gets run every time I open a new terminal, which is not what I want for the logger.
Is there a way to tell in Bash that the current login is a gnome login? Alternatively, is there some sort of .gnomerc I can use to run my script?
Edit: Here is my script:
Edit 2: Removed the script, since it's not related to the question. I will repost my other question, rather than repurpose this one.
Are you looking for a way to detect what type of terminal it is?
Try:
echo $TERM
From Wikipedia:
TERM (Unix-like) - specifies the type of computer terminal or terminal
emulator being used (e.g., vt100 or dumb).
See also: List of Terminal Emulators
for bash use : ~/.bash_logout
that will get executed when you logout, which sounds like what you are trying to do.
Well, for just bash, what you want are .bash_login/.bash_logout in your home directory (rather than .bashrc) These are run whenever a LOGIN shell starts/finishes, which happens any time you log in to a shell (on a tty or console, or via ssh or other network login). These are NOT run for bash processes created to run in terminal windows that you create (as those are not login shells) so won't get run any time you open a new terminal.
The problem is that if you log in with some mechanism that does not involve a terminal (such as gdm running on the console to start a gnome or kde or unity session), then there's no login shell so .bash_login/logout never get run. For that case, the easiest is probably to put something in your .xsessionrc, which will get run every time you start an X session (which happens for any of those GUI environments, regardless of which one you run). Unfortunately, there's no standard script that runs when an X session finishes.

Find the current stdout OR How to redirect the output back to console

I'm using Ubuntu 9.04 x64 and,
I have a file startup.rb in which I call sudo bash, so that I always have a root console to do administrative tasks without typing password after every 15 minutes or so.
This script is called by another script Startup.rb, and content of both files are like this -
File ~/Startup.rb
#!/usr/bin/ruby
system "gnome-terminal --maximize -x ruby ~/startup.rb"
File ~/startup.rb
#!/usr/bin/ruby
`sudo some-repetitive-administrative-task`
....
....
`sudo bash` #Not using `sudo -i`, since that makes `pwd` -> /root
I have included the ~/Startup.rb file in Startup Applications list.
Now the problem is that, in the terminal of sudo bash, if I type something and expect some output, I don't get any. So if I write echo hello world, I don't get any output. Which leads me to believe that the standard output (stdout) of the sudo bash command is not the console.
So, I want to know why is this happening? How may I know the current stdout path? Or how can I restore stdout back to my current console?
-- thanks
You're using an inapproriate method to run system commands from Ruby. Try this instead:
#!/usr/bin/ruby
system 'bash'
The syntax you're using (with the backticks) captures the standard output of the command and returns it in a string. That's why you don't see it on the terminal.
Here's a nice review of the different ways to run commands from Ruby: 6 Ways to Run Shell Commands in Ruby.
If you are only interested in an easier way to run administrative tasks as root, there might be a few other things you might consider.
$sudo -s
This will give you a shell in which you can submit commands as your sudo'd self (assuming that sudo has been setup so that you can run a shell via sudo).
Another thing you can do, though not always recommended or considered good form in Ubuntu is to create a root account:
$sudo passwd root
then you can login as root and administer things that way.
I know this doesn't answer your specific question about ruby, but I hope you find it helpful.

Resources