Use Bash's select from within Python - bash

The idea of the following was to use Bash's select from Python, e.g. use Bash select to get the input from the user, communicate with the Bash script to get the user selections and use it afterwords in the Python code. Please tell me if it at least possible.
Have the following simple Bash script:
#!/bin/bash -x
function select_target {
target_list=("Target1" "Target2" "Target3")
PS3="Select Target: "
select target in "${target_list[#]}"; do
break
done
echo $target
}
select_target
it works standalone
Now I tried to call it from Python like this:
import tempfile
import subprocess
select_target_sh_func = """
#!/bin/bash
function select_target {
target_list=(%s)
PS3="Select Target: "
select target in "${target_list[#]}"; do
break
done
echo $target
}
select_target
"""
target_list = ["Target1", "Target2", "Target3"]
with tempfile.NamedTemporaryFile() as temp:
temp.write(select_target_sh_func % ' '.join(map(lambda s : '\"%s\"' % str(s),target_list)))
subprocess.call(['chmod', '0777', temp.name])
sh_proc = subprocess.Popen(["bash", temp.name], stdout=subprocess.PIPE)
(output, err) = sh_proc.communicate()
exit_code = sh_proc.wait()
print output
It does nothing. No output, no selection.
I'm using High Sierra MacOS, PyCharm and Python 2.7.
PS
After some reading and experimenting ended up with the following:
with tempfile.NamedTemporaryFile() as temp:
temp.write(select_target_sh_func % ' '.join(map(lambda s : '\"%s\"' % str(s),target_list)))
temp.flush()
# bash: /var/folders/jm/4j4mq_w52bx2l5qwg4gt44580000gn/T/tmp00laDV: Permission denied
subprocess.call(['chmod', '0500', temp.name])
sh_proc = subprocess.Popen(["bash", "-c", temp.name], stdout=subprocess.PIPE)
(output, err) = sh_proc.communicate()
exit_code = sh_proc.wait()
print output
It behaves as I expected it would, the user is able to select the 'target' by just typing the number. My mistake was that I forgot to flush.
PPS
The solution works for MacOS X High Sierra, sadly it does not for Debian Jessie complaining the following:
bash: /tmp/tmpdTv4hp: Text file busy
I believe it is because `with tempfile.NamedTemporaryFile' keeps the temp file open and this somehow prevents Bash from working with it. This renders the whole idea useless.

Python is sitting between your terminal or console and the (noninteractive!) Bash process you are starting. Furthermore, you are failing to direct the standard output pipe anywhere, so subprocess.communicate() actually cannot capture standard error (and if it could, you would not be able to see the script's menu).
Running an interactive process programmatically is a nontrivial scenario; you'll want to look at pexpect or just implement your own select command in Python - I suspect this is going to turn out to be the easiest solution (trivially so if you can find an existing library).

Related

Cannot write files from Lua in Scite on Windows 10?

Using Scite 4.1.3 on Windows 10.
I tried the following Lua script:
function TestFile()
mycontent = "Hello World"
mytmpfilename = os.tmpname()
os.execute("echo aaaa > " .. mytmpfilename) -- create file explicitly;
mytmpfile = io.popen(mytmpfilename, "w") -- w+ crashes Scite in Windows!
mytmpfile:write(mycontent)
mytmpfile:close()
print("readall " .. mytmpfilename .. ": " .. io.popen(mytmpfilename, "r"):read("*a"))
end
If I run this, I get printed:
readall C:\Users\ME\AppData\Local\Temp\s8qk.m:
... which means Lua could not even read this file?! And also, this stupid Windows Explorer prompt shows up:
At end, the content of the C:\Users\ME\AppData\Local\Temp\s8qk.m is still just aaaa.
So obviously, mytmpfile:write part fails silently, and nothing new is written in the file - the only thing that wrote to the file is the echo aaaa > ... executed by cmd.exe via os.execute.
So my question is - how can I write a file with Lua in Scite on Windows? Preferably, without having that stupid "How do you want to open this file?" prompt show up?
Eh, I think I got it ...
See, the OP example uses io.popen - and, if we look at https://man7.org/linux/man-pages/man3/popen.3.html it says:
popen, pclose - pipe stream to or from a process
(emphasis mine).
So, basically, if under Windows I try to do io.popen(filename), then apparently tries to find the process that would be the default "opener" for that file type ... and also therefore the prompt I'm being shown in the OP (and therefore, I could never read or write the files accessed -- or rather, not accessed -- in that way).
However, Programming in Lua : 21.2 – The Complete I/O Model actually uses io.open (notice, no p for process); and then the files seem to open for read/write fine.
So, corrected example from OP should be:
function TestFile()
mycontent = "Hello World"
mytmpfilename = os.tmpname()
-- os.execute("echo aaaa > " .. mytmpfilename) -- create file explicitly; -- no need anymore, with io.open
mytmpfile = io.open(mytmpfilename, "w+") -- w+ crashes Scite in Windows, but only if using io.popen; is fine with io.open!
mytmpfile:write(mycontent)
mytmpfile:close()
print("readall " .. mytmpfilename .. ": " .. io.open(mytmpfilename, "r"):read("*a"))
end

How can I store "image lookup -v address" result inside a variable?

I am able to symbolicate symbol address through following lldb command:
image lookup --address $SYMBOL_ADDRRESS
But while writing a shell script to parse, I am not able to find a way to store the output of above command into a variable or file.
First off, if your script's job is mostly about driving lldb and you happen to know Python, you will be much happier using the lldb module in Python, where you can drive the debugger directly, than getting lldb to produce text output which you parse in the shell script.
The lldb Python module provides API's like SBTarget.ResolveSymbolContextForAddress, which runs the same lookup as image lookup --address but returns the result as a Python lldb.SBSymbolContext object, which you can either query for module/file/line etc using API's on the object. So getting bits of info out of this result will be easier with the lldd API's.
But if you have to use a shell script, then the easiest thing is probably to write the command output to a file and read that back into the shell script. lldb doesn't have generic support for tee-ing command output into a log file yet, but the lldb Python module allows you to run command-line commands and programmatically capture the output.
So you can do it easily from lldb's Python script interpreter:
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> result = lldb.SBCommandReturnObject()
>>> lldb.debugger.GetCommandInterpreter().HandleCommand("image lookup -va $pc", result)
2
>>> fh = open("/tmp/out.txt", "w")
>>> fh.write(result.GetOutput())
>>> fh.close()
>>> quit
(lldb) plat shell cat /tmp/out.txt
Address: foo[0x0000000100003f6f] (foo.__TEXT.__text + 15)
Summary: foo`main + 15 at foo.c:6:3
Module: file = "/tmp/foo", arch = "x86_64"
CompileUnit: id = {0x00000000}, file = "/tmp/foo.c", language = "c99"
Function: id = {0x7fffffff00000032}, name = "main", range = [0x0000000100003f60-0x0000000100003f8a)
FuncType: id = {0x7fffffff00000032}, byte-size = 0, decl = foo.c:4, compiler_type = "int (void)"
Blocks: id = {0x7fffffff00000032}, range = [0x100003f60-0x100003f8a)
LineEntry: [0x0000000100003f6f-0x0000000100003f82): /tmp/foo.c:6:3
Symbol: id = {0x00000005}, range = [0x0000000100003f60-0x0000000100003f8a), name="main"
You can also write a lldb command in Python that wraps this bit of business, which would make it easier to use. Details on that are here:
https://lldb.llvm.org/use/python-reference.html#create-a-new-lldb-command-using-a-python-function
You could even do a hybrid approach, and make all the lldb work you want to do a custom Python command. That would allow you to use the lldb Python API's to get what info you needed and write it out in whatever format is convenient for you, and would simplify the lldb invocation in your shell script and facilitate recovering the information lldb provided...

_curses.error: setupterm: could not find terminal, Raspberry Pi

Every time I run this script on my Raspberry Pi:
import curses
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
motor1a = 7
motor1b = 11
motor1e = 22
motor2a = 13
motor2b = 16
motor2e = 15
GPIO.setup(motor1a,GPIO.OUT)
GPIO.setup(motor1b,GPIO.OUT)
GPIO.setup(motor1e,GPIO.OUT)
GPIO.setup(motor2a,GPIO.OUT)
GPIO.setup(motor2b,GPIO.OUT)
GPIO.setup(motor2e,GPIO.OUT)
screen = curses.initscr()
curses.noecho()
curses.cbreak()
curses.halfdelay(3)
screen.keypad(True)
try:
while True:
char = screen.getch()
if char == ord('q'):
break
elif char == curses.KEY_UP:
GPIO.output(motor1a,GPIO.HIGH)
GPIO.output(motor1b,GPIO.LOW)
GPIO.output(motor1e,GPIO.HIGH)
GPIO.output(motor2a,GPIO.HIGH)
GPIO.output(motor2b,GPIO.LOW)
GPIO.output(motor2e,GPIO.HIGH)
# except SOMEEXCEPTION is missing here, I am not sure why there is an exception in the first place
I get an error:
_curses.error: setupterm: could not find terminal
How can I fix this?
I saw a post where it said to do the following:
You must set environment variables TERM and TERMINFO, like this:
export TERM=linux ; export TERMINFO=/etc/terminfo
But I'm not sure where to do that step.
In order to use curses, you need to tell which terminal you are using so that the library can send correct commands. This is done by running the commands you provided in a shell, at the same place where you run your program
$ export TERM=linux
$ export TERMINFO=/etc/terminfo
$ python3 myprogram.py
($ is the prompt of the shell, you may have something else)
As I mentioned in my comment, your code will not run anyway, there is an except missing after the try (I am not sure what you need the try for, and you will need to understand this anyway in order to catch the right exception)

Can I fully customise an Xcode 4 Run Script Build Phase error/warning within the Issues Navigator and Build Logs?

I read on a blog somewhere that you can integrate your own build scripts with Xcode's Issues Navigator and Build Logs GUIs by printing messages to STDOUT using the following format:
FILENAME:LINE_NUMBER: WARNING_OR_ERROR: MSG
(Where WARNING_OR_ERROR is either warning or error)
e.g.
/path/to/proj/folder/somefile.ext:10: warning: There was a problem processing the file
Will show a Warning at line 10 of somefile.ext which reads "There was a problem processing the file". This does actually work (which is fantastic).
Is there any official documentation of this feature (I couldn't find any)?
In the Issues Navigator, I get a warning for the file somefile.ext, but the issue's title is "Shell Script Invocation Error" (my message appears underneath the title). Is there some way to set that heading, or am I stuck with that generic (and ugly) "Shell Script Invocation Error"?
It doesn't really answer your question as to whether you can customise the "Shell Script Invocation Error", but perl code doesn't get the nice error messages you describe, however if you include this perl module (or just the code from it) in your perl script, it does generate the nice error messages you talk about (still the same "Shell Script Invocation Error" title you mention). Just thought I'd share it for anyone using a perl script in Xcode and getting really lousy errors.
package XcodeErrors;
use strict;
use warnings;
$SIG{__WARN__} = sub
{
my #loc = caller(0);
print STDERR "$loc[1]:$loc[2]: warning: ", #_, "\n";
return 1;
};
$SIG{__DIE__} = sub
{
my #loc = caller(0);
print STDERR "$loc[1]:$loc[2]: error: ", #_, "\n";
exit 1;
};
1;
exit with 0 in your customized shell script will turn off "Shell Script Invocation Error"

How can I get the last modified time of a directory in Perl on Windows?

In Perl (on Windows) how do I determine the last modified time of a directory?
Note:
opendir my($dirHandle), "$path";
my $modtime = (stat($dirHandle))[9];
results in the following error:
The dirfd function is unimplemented at scriptName.pl line lineNumber.
Apparently the real answer is just call stat on a path to the directory (not on a directory handle as many examples would have you believe) (at least for windows).
example:
my $directory = "C:\\windows";
my #stats = stat $directory;
my $modifiedTime = $stats[9];
if you want to convert it to localtime you can do:
my $modifiedTime = localtime $stats[9];
if you want to do it all in one line you can do:
my $modifiedTime = localtime((stat("C:\\Windows"))[9]);
On a side note, the Win32 UTCFileTime perl module has a syntax error which prevents the perl module from being interpreted/compiled properly. Which means when it's included in a perl script, that script also won't work properly. When I merge over all the actual code that does anything into my script and retry it, Perl eventually runs out of memory and execution halts. Either way there's the answer above.
my $dir_path = "path_of_your_directory";
my $mod_time = ( stat ( $dir_path ) )[9];
Use the Win32::UTCFileTime module on CPAN, which mirrors the built-in stat function's interface:
use Win32::UTCFileTime qw(:DEFAULT $ErrStr);
#stats = stat $file or die "stat() failed: $ErrStr\n";

Resources