__file__ = './00_core.ipynb'
pshnb IPython magic
psh
persistent bash magics in Jupyter and IPython
Foundations
= dict(os.environ, TERM='dumb', PS1='$', PS2='$')
env = os.environ['SHELL']
eshell = pexpect.spawn(eshell, encoding='utf-8', env=env) sh
This cell creates the initial shell process using pexpect.spawn()
with a basic environment configuration. The key setup includes:
- Environment variables: Sets
TERM='dumb'
to prevent terminal formatting issues, and standardizes prompts withPS1='$'
andPS2='$'
- Shell spawning: Creates a persistent shell process using the user’s default shell (
$SHELL
) with UTF-8 encoding - Process persistence: The
sh
object maintains a continuous connection to the shell, allowing commands to be sent and responses captured across multiple interactions
PS1 is the primary shell prompt (what you see when the shell is ready for a command), and PS2 is the secondary prompt (shown when a command spans multiple lines or is incomplete). pexpect
is a Python library for controlling interactive command-line programs by automating the sending of inputs and reading of outputs. It’s particularly useful for automating tasks that normally require human interaction. spawn
is pexpect’s main function that starts a child process (like a shell) and returns a pexpect object that can communicate with it. Unlike subprocess
, spawn maintains an interactive session - you can send commands, wait for responses, and continue the conversation with the same process instance.
This establishes the foundation for persistent shell state - variables, directory changes, and other shell modifications will persist between command executions.
= dict(os.environ, TERM='dumb', PS1='', PS2='')
env = pexpect.spawn(eshell, encoding='utf-8', env=env)
sh 'stty -echo')
sh.sendline(
sh.readline()= os.urandom(8).hex()
echo = re.compile(fr'^{echo}\s*$', flags=re.MULTILINE)
echo_re f'export PS1=""')
sh.sendline('set +o vi +o emacs')
sh.sendline('echo '+echo)
sh.sendline(=2) sh.expect(echo_re, timeout
0
This cell implements a more sophisticated shell setup that addresses output formatting and command echo issues:
- Echo suppression: Uses
stty -echo
to prevent command echoing, which is crucial for clean output capture - Empty prompts: Sets
PS1=""
andPS2=""
to eliminate prompt strings that would interfere with output parsing - Line editing disabled: Runs
set +o vi +o emacs
to disable interactive line editing features that can cause issues in programmatic shell interaction - Synchronization mechanism: Establishes a unique echo token using
os.urandom(8).hex()
and corresponding regex pattern for reliable command completion detection
The echo token system is particularly important - it provides a reliable way to detect when a command has finished executing by sending a unique marker and waiting for it to appear in the output.
'ls | head -3')
sh.sendline('echo '+echo)
sh.sendline(=2)
sh.expect(echo_re, timeoutprint(sh.before)
00_core.ipynb
CHANGELOG.md
index.ipynb
This cell demonstrates the practical application of the configured shell for command execution and output capture:
- Command execution: Sends
ls | head -3
to list the first three files in the current directory - Completion detection: Uses the echo token mechanism (
echo [random_hex]
) followed bysh.expect(echo_re)
to reliably detect when the command has finished - Clean output extraction: Captures the command output via
sh.before
, which contains everything received before the expected echo token
This pattern shows how the kernel can execute arbitrary bash commands while maintaining clean separation between command output and shell control mechanisms. The timeout parameter (2 seconds) provides protection against hanging commands, which is essential for a responsive interactive environment.
ShellInterpreter
ShellInterpreter (debug=False, timeout=2, shell_path=None, sudo=False, dumb=True)
Initialize self. See help(type(self)) for accurate signature.
= ShellInterpreter()
sh print(sh('ls | head -3'))
00_core.ipynb
CHANGELOG.md
index.ipynb
NB: requires passwordless sudo to use this:
# sh = ShellInterpreter(sudo=True)
# sh('cd')
# print(sh('pwd'))
# sh('cd ..')
# print(sh('pwd'))
# print(sh('whoami'))
shell_replace
shell_replace (s, shell=None)
Replace @{var}
refs in s
with their variable values, if they exist
= 1
b
= '''asdf
a $@{b} @{aa}
fdsa'''
print(shell_replace(a))
asdf
$1 @{aa}
fdsa
PshMagic
PshMagic (shell, sudo=False, timeout=2, expand=True, o=None)
Initialize self. See help(type(self)) for accurate signature.
These changes allow -r
to work as before (reset with default shell) and also accept an optional shell path like -r /bin/zsh
or --reset=/bin/bash
.
create_magic
create_magic (shell=None)
# Only required if you don't load the extension
create_magic()
%bash pwd
/Users/jhoward/aai-ws/pshnb
%bash ls -h
00_core.ipynb LICENSE pshnb.egg-info settings.ini
CHANGELOG.md MANIFEST.in pyproject.toml setup.py
index.ipynb pshnb README.md styles.css
%bash cd ..
%bash pwd
/Users/jhoward/aai-ws
%%bash
cat > tmp << EOF
hi
there
EOF
%bash cat tmp
hi
there
%bash rm tmp
%bash ls | head -3
_nbs
_proc
addnew.py
= 2 n
%bash echo @{n}
2
%bash ls | head -@{n}
_nbs
_proc
%%bash
echo starting
(sleep 1; echo finished) &
starting
[1] 18411
%bash
finished
[1]+ Done ( sleep 1; echo finished )
%bash -h
::
%bash [-h] [-r [RESET]] [-o] [-x] [-X] [-s] [-S] [-t TIMEOUT]
[command ...]
Run line or cell in persistent shell
positional arguments:
command The command to run
options:
-h, --help Show this help
-r <[RESET]>, --reset <[RESET]>
Reset the shell interpreter (optionally choose shell)
-o, --obj Return this magic object
-x, --expand Enable variable expansion
-X, --no-expand Disable variable expansion
-s, --sudo Enable sudo
-S, --no-sudo Disable sudo
-t TIMEOUT, --timeout TIMEOUT
Set timeout in seconds
%bash pwd
/Users/jhoward/aai-ws
Reset the interpreter:
%bash -r
%bash pwd
/Users/jhoward/aai-ws/pshnb
%bash echo $SHELL
/opt/homebrew/bin/bash
sudo:
%bash -s
%bash whoami
root
no sudo:
%bash -S
%bash whoami
jhoward
timeout:
%bash -t 1
try: get_ipython().run_line_magic('bash', 'sleep 2')
except TIMEOUT: print("timed out")
timed out
load_ipython_extension
load_ipython_extension (ipython)
Required function for creating magic
create_ipython_config
create_ipython_config ()
Called by pshnb_install
to install magic