Why does the value returned by python script gets modified by the shell? - bash

My pythonscript looks like this
#!/usr/bin/python3
#File Name: pythonScript.py
from sys import exit
if '__name__'=='__main__':exit(402)
And this is the shell script
python3 pythonScript.py
echo $?
It prints 146. How does 402 get mapped to 146? Some other pairs such as this are (402, 146), (100,0), (0, 0), (56, 0) etc.
Can the python script return value to shell this way, and is the ? the correct variable to capture this?
My machine version is this, if this is important.
4.4.0-89-generic #112-Ubuntu SMP Mon Jul 31 19:38:41 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

in bash, the exit code is visible only the lowermost 8 bits, low 8 bits of 402 is 146:
>>> 402 & 0b11111111
146

It's not the shell. The system call interface only accommodates an unsigned 8-bit number; so the maximum value which can be communicated is 255.
The same thing would happen if you wrote a C program whose int main() tried to return(402); and you called it from another C program with exec, without any shell.

Related

How does bash handle control characters?

I'm writing a shell that tries to simulate bash's behaviour. The problem is that I've read that when Bash gets the user input, it uses a non-canonical mode and turn off the ECHO kind of like this :
(*conf)->newterm.c_lflag &= ~(ICANON | ECHO);
But if it turns ECHO off, how is SIGINT still displayed as ^C on the temrinal ? When I try this, I get the character � which is -1 (if you try to print it).
If we pay enough attention to bash behaviour, control characters are never displayed except for ctrl-c AKA SIGINT. My only theory is that bash hard coded ^C and just print it to the screen when SIGINT is detected. Am I right saying this ? Of not, how does BASH or ZSH display ^C having ECHO and ICANON turned off ? I've looked everywhere and I really can't seem to find an answer to this...
Thank you for your time and your explanation !
EDIT :
Here is what I do. First I initialize my shell as this :
tcgetattr(STDIN_FILENO, &(*conf)->oldterm);
(*conf)->newterm = (*conf)->oldterm;
(*conf)->newterm.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &(*conf)->newterm); // Apply immediately
Then well I press ctrl-c, I get a newline and ^C is not displayed. This is because I'm disabling ECHO so ECHOCTL gets disabled too. Now, What I want to have is if I press ctr-c, have ^C displayed, but also not having ^[[A when I press the upper arrow key. I'm just unsure how to configure termios to do that.
I believe the ^C is actually being generated by the terminal device interface (tty), as opposed to Bash or Z Shell. For example, and following stty -echoctl and stty -echo, ^C is no longer displayed. As another example, a simple C program with sleep(1)s in an infinite loop and SIGINT set to SIG_IGN (and with stty echo), still displays ^C.
This example works for me, the most relevant lines being 17, 19, 20, 36, and 37 (gcc -Wall compiled, in macOS and Linux, in Bash and Z Shell, with Terminal [Apple], Terminator, and GNOME Terminal, within and without GNU Screen and/or tmux, and with TERMs of xterm-256color and screen-256color):
1 #include <errno.h>
2 #include <signal.h>
3 #include <stdio.h>
4 #include <termios.h>
5 #include <unistd.h>
6
7 void sh( int _sig )
8 {
9 puts("SIGINT");
10 }
11
12 int main()
13 {
14 struct termios t;
15 void (*s)(int);
16
17 if ( tcgetattr(fileno(stdin),&t) )
18 return(1);
19 t.c_lflag &= ~ECHO;
20 if ( tcsetattr(fileno(stdin),TCSANOW,&t) )
21 return(2);
22 if ( ( s = signal(SIGINT,&sh) ) == SIG_ERR )
23 return(3);
24
25 /**
26 ** While the following `sleep` is running, [ctrl]+c will
27 ** trigger a SIGINT, which will lead to a `sh(SIGINT)` call,
28 ** which will lead to "SIGINT\n" being written to STDOUT,
29 ** and `sleep` will be interrupted, returning sleep seconds
30 ** remaining. "^C" should not be printed to the terminal.
31 **/
32 puts("Tap [ctrl]+c within 60 seconds and expect \"SIGINT\"...");
33 if ( sleep(60) == 0 || errno != EINTR )
34 return(4);
35
36 t.c_lflag |= ECHO;
37 if ( tcsetattr(fileno(stdin),TCSANOW,&t) )
38 return(5);
39 if ( signal(SIGINT,s) == SIG_ERR )
40 return(6);
41
42 /**
43 ** With the default SIGINT handler restored, the following will
44 ** only return if the time fully elapses. And, with the ECHO
45 ** bit restored, "^C" should be printed to the terminal (unless
46 ** such echo'ing was otherwise disabled).
47 **/
48 puts("Tap [ctrl]+c within 60 seconds, expect \"^C\", and expect a 130 return (128+SIGINT)...");
49 sleep(86400);
50
51 return(7);
52 }
P.S. Keep in mind that [ctrl]-c (generally) triggers a SIGINT, that SIGINT can be generated without a [ctrl]-c, and that, for example, kill -INT "$pid", should not trigger a tty-driven ^C output.
If Bash does use some non standard input mode, it's probably just to support line editing, and not part of its core functionality as a shell. I think a traditional unix shell would just read lines of input, exectute them, and then prompt. An interactive shell would capture sigint so that the shell wouldn't log you out when you hit ^C, but that's about it. Quite a few things that you might imagine are handled in the shell, are actually not, they're in the kernel, in terminal drivers, input drivers and so forth.
Why are you trying to emulate bash, is this for fun and learning? If you want to do that, you should go start by looking at the source code of older and simpler shells. You could certainly go look at the old bsd "csh" code, or maybe the original bourne shell code. These shells do the essence of what it is to be a shell. There's been even simpler shells if you look for them. Later shells complicate things by adding line editing, and if you want to understand those, you can get the gnu readline library, and add that to your own app. You could add it to your shell for that matter.

What is pylint exit code 28?

Pylint exited with code 28 on Linux run. What does this error code mean?
I know it doesn't mean low on space as I tried the same command on a empty VM.
Answer + Examples:
(Note the pylint exit codes table below from pylint official site.
pylint exit codes conforms to 2 to the power of x (x = 0 to 5)
This is a crystal clear sign that they use a binary system for exit codes, this means that (as mentioned in the example within the image below) if exit code is:
28 - in binary: 0001 1100 (exit code 4, 8, 16 was triggered at least once for each)
63 - in binary: 0011 1111 (all error codes was triggered at least once for each which is the maximum errors combined)
Pyline exit code 28 on Linux and exit code 30 on Windows both mean that the config file that you are feeding in has an invalid configuration.
For me that was having underscores separating words instead of dashes. I had changed this because the wheel build deprecated dashes for flake8 and mypy but pylint needs to remain with dashes separating the words.

Using expect() and interact() simultaneously in pexpect

The general problem is, that I want to use pexpect to call scripts that require sudo rights, but I don't always want to enter my password (only once).
My plan is to use pexpect to spawn a bash session with sudo rights and to call scripts from there. Basically I always want to keep the session busy, whenever one script stopped, I want to start another. But while the scripts are running, I want the user to have control. Meaning:
The scripts should be called after something like expect("root#"), so whenever the session is idle, it starts another script. While the scripts are running interact() is giving the user control of possible input he wants to give.
My idea was to use different threads to to solve this problem. My code (for the proof of concept) looks like this:
import pexpect
import threading
class BashInteractThread(threading.Thread):
def __init__(self, process):
threading.Thread.__init__(self)
self.pproc = process
def run(self):
self.pproc.interact()
s = pexpect.spawn("/bin/bash", ['-i', '-c', "sudo bash"])
it = BashInteractThread(s)
it.start()
s.expect("root#")
s.sendline("cd ~")
while(s.isalive()):
pass
s.close()
When I call this script, it does not give me any output, but the process seems to have been started. Still, I cannot CTRL-C or CTRL-D to kill the process - I have to kill the process separately. The behavior I would expect, would be to get prompted to enter a password and after that it should automatically change the directory to the home directory.
I don't exactly know why it does not work, but I guess the output only gets forwarded either to interact() or to expect().
Does anyone have an idea on how to solve this? Thanks in advance.
You can take advantage of interact(output_filter=func). I just wrote a simple example (no coding style!). What it does is spawn a Bash shell and repeatedly invoke Python for the user to interact with. To exit the trap, just input (or print) the magic words LET ME OUT.
expect() would not work anymore after interact(), so need to do the pattern matching work manually.
The code:
[STEP 101] # cat interact_with_filter.py
import pexpect, re
def output_filter(s):
global proc, bash_prompt, filter_buf, filter_buf_size, let_me_out
filter_buf += s
filter_buf = filter_buf[-filter_buf_size:]
if "LET ME OUT" in filter_buf:
let_me_out = True
if bash_prompt.search(filter_buf):
if let_me_out:
proc.sendline('exit')
proc.expect(pexpect.EOF)
proc.wait()
else:
proc.sendline('python')
return s
filter_buf = ''
filter_buf_size = 256
let_me_out = False
bash_prompt = re.compile('bash-[.0-9]+[$#] $')
proc = pexpect.spawn('bash --noprofile --norc')
proc.interact(output_filter=output_filter)
print "BYE"
[STEP 102] #
Let's try it:
[STEP 102] # python interact_with_filter.py
bash-4.4# python
Python 2.7.9 (default, Jun 29 2016, 13:08:31)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> exit() <---- user input
bash-4.4# python
Python 2.7.9 (default, Jun 29 2016, 13:08:31)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> exit() <---- user input
bash-4.4# python
Python 2.7.9 (default, Jun 29 2016, 13:08:31)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> LET ME OUT <---- user input
File "<stdin>", line 1
LET ME OUT
^
SyntaxError: invalid syntax
>>> exit() <---- user input
bash-4.4# BYE
[STEP 103] #

Does BASH's ability to handle numbers scale with processor bitwidth?

I recently had to write a library for BASH to handle large numbers because I had to glue some software outputs together that needed basic arithmetic. If I was writing a shell, I would not assume that most people would need 4096-bit arithmetic. However, I have two machines that both can handle up to 64-bit arithmetic in BASH, but my question is how this is related to CPUs.
On a 32-bit processor, is BASH's maximum arithmetic capability 32-bits, and on a 16-bit processor or 8-bit processor with a some OS and BASH would the maximum numerical capability be 16-bits and 8-bits respectively?
The BASH manual did not shed any light onto what the limits would be.
Bash's internal arithmetic is the bitwidth of the target for which bash was compiled, which may be less than the bitwidth of the processor on which it's running. Specifically, bash's internal arithmetic is intmax_t per this bug-report comment. The intmax_t type is
a signed integer type capable of representing any value of any signed integer type
per the docs, and is defined by the compiler. Therefore, bash arithmetic is limited by the compilation environment. Even on a 64-bit processor, a bash version compiled for a 32-bit target will be limited to 32 bits.
Details: in expr.c, the source says
All arithmetic is done as intmax_t integers with no checking for overflow (though division by 0 is caught and flagged as an error).
Edit Here's a runtime test for bit width that (I think) only relies on bash being signed two's complement. Works on my system - YMMV.
#!/bin/bash
for((i=1; $i>0; i*=2)); do : ; done
# Set the highest-order bit, which is a negative number in signed math.
echo "$(echo "l(-($i))/l(2)"|bc -l)/1+1" | bc
# Output the position of that bit
The inner bc -l computes log2(|$i|), the index of the highest-order bit plus just a little bit since the two's complement range is asymmetric around zero. The outer bc computes the ceiling of that value. (bc when run without -l truncates divisions - thanks to this answer). On my system, this outputs 64.
Empirical test:
~$ uname -a --- NOTE: 64-bit Cygwin vvvvvv
CYGWIN_NT-6.3 localhost 2.5.2(0.297/5/3) 2016-06-23 14:29 x86_64 Cygwin
~$ bash --version
GNU bash, version 4.3.46(6)-release (x86_64-unknown-cygwin)
<cut> --- NOTE: 64-bit bash ^^^^^^
~$ echo $(( 2 ** 32 ))
4294967296 <--- so >32 bit
~$ echo $(( 2 ** 64 ))
0 <--- oops - bigger than 64 bit doesn't work
~$ echo $(( 2 ** 63 ))
-9223372036854775808 <--- but 64 bit does (and is signed)

Problem with Ant's AnsiColorLogger in Snow Leopard

I have Ant configured to use the AnsiColorLogger. In Mac OS 10.5, everything was fine. Since upgrading to Snow Leopard, the AnsiColorLoggger no longer works. I see the Ant output (uncolorized) for a second then it just disappears. Has anyone else gotten this working in Snow Leopard? Other ANSI colors are working fine in Terminal.app (colored ls output, colors in my prompt).
Also, would this be a better question on SuperUser?
UPDATE: I have sorted out the issue. It has to do with ANT giving escape sequences that while appropriate for a linux xterm, are NOT correctly interpreted by Mac OS X. It is possible to filter the ANT output to convert these sequences and restore colorized output.
The moral of the story is that this wrapper script will achieve colorized output:
# cat /workspace/SDK/bin/ant-wrapper.sh
/usr/bin/ant -logger org.apache.tools.ant.listener.AnsiColorLogger "$#" | perl -pe 's/(?&lt=\e\[)2;//g'
# alias ant='/workspace/SDK/bin/ant-wrapper.sh'
# ant publish
(output has lots of pretty colors; well, maybe not so pretty, more like an easter egg)
Original Post (and debugging steps):
I'm having similar issues with regard to AnsiColorLogger not displaying colors at all. I'm not sure what the author means by "[output appears] for a second then it just disappears". That seems like a strange problem to occur on the Terminal.
My Box:
# uname -a
Darwin Dave-Dopsons-MacBook-Pro.local 10.7.0 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386
This is the ANT Logger we are using:
http://ant.apache.org/manual/listeners.html#AnsiColorLogger
Here's a related forum post (tried the advice given, to no avail): http://ant.1045680.n5.nabble.com/Macosx-and-AnsiColorLogger-td1355310.html
I did "ant | less", and I DO see escape sequences, but still no colors:
Buildfile: /workspace/Words/words_blackberry/build.xml
ESC[2;32m
publish:ESC[m
Still blocked on this, and would love advice if anyone has gotten it to work on OSX
GOT IT!
So here's the output of colorized ls:
# CLICOLOR_FORCE=exfxcxdxbxegedabagacad ls -lGF | less
total 112
-rw-r--r-- 1 ddopson admin 6511 May 29 12:41 build.xml
drwxr-xr-x 6 ddopson admin 204 May 28 23:59 ESC[34meclipse-binESC[mESC[m/
lrwxr-xr-x 1 ddopson admin 35 May 23 21:24 ESC[35mfilesESC[mESC[m# -> ../artwork/output/blackberry/files/
lrwxr-xr-x 1 ddopson admin 36 May 23 21:20 ESC[35mimagesESC[mESC[m# -> ../artwork/output/blackberry/images/
Notice how the escape sequences are subtly different; they don't have the '2;' like ANT did...
So to test this theory:
ant -logger org.apache.tools.ant.listener.AnsiColorLogger publish | sed 's/2;//g'
... and the output is COLORIZED! Victory!
I've take ddopson's knowledge and crammed it into a single line:
ant () { command ant -logger org.apache.tools.ant.listener.AnsiColorLogger "$#" | sed 's/2;//g' ; }
This works by using a Bash Function. Place this in your ~/.profile file and it will do the same thing as ddopson's ant-wrapper.sh, but without needing a second file to make it work. Slightly more elegant and less fragile.

Resources