Skip to content

Commit a112a8a

Browse files
committed
updates showboat demo to use /tmp/bash-demo
1 parent 0c1475d commit a112a8a

File tree

1 file changed

+22
-20
lines changed

1 file changed

+22
-20
lines changed

demo/showboat-notes/bash-sandbox-exec.md

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ The sandbox exposes a `bash()` function that lets Python scripts run shell comma
1010
Every `bash()` call returns a dict with three keys: `stdout`, `stderr`, and `exit_code`. A zero exit code means success.
1111

1212
```bash
13-
cat > /tmp/bash_return.py << 'EOF'
13+
mkdir -p /tmp/bash-demo
14+
cat > /tmp/bash-demo/bash_return.py << 'EOF'
1415
result = bash("echo hello from bash")
1516
print("stdout: " + repr(result["stdout"].strip()))
1617
print("stderr: " + repr(result["stderr"]))
1718
print("exit_code: " + str(result["exit_code"]))
1819
EOF
19-
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_return.py
20+
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_return.py
2021

2122
```
2223

@@ -31,7 +32,7 @@ exit_code: 0
3132
Shell state does not persist between calls, but within a single call you can chain commands with `&&` and pipes. Here we count lines in the BED files and peek at the first few records.
3233

3334
```bash
34-
cat > /tmp/bash_inspect.py << 'EOF'
35+
cat > /tmp/bash-demo/bash_inspect.py << 'EOF'
3536
# wc on both BED files
3637
r = bash("wc -l swipe.bed demo-swipe-output.bed")
3738
print("wc -l:\n" + r["stdout"])
@@ -47,7 +48,7 @@ r = bash("awk '$6==\"-\"' swipe.bed | wc -l")
4748
minus = r["stdout"].strip()
4849
print("plus-strand: " + plus + " minus-strand: " + minus)
4950
EOF
50-
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_inspect.py
51+
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_inspect.py
5152

5253
```
5354

@@ -70,13 +71,13 @@ plus-strand: 5 minus-strand: 5
7071
Shell state does not persist between `bash()` calls (cwd, variables, etc.), so multi-step work must be done in a single call using `&&` or pipes. Here we compute the fragment-length distribution from the BED file entirely in shell.
7172

7273
```bash
73-
cat > /tmp/bash_pipeline.py << 'EOF'
74+
cat > /tmp/bash-demo/bash_pipeline.py << 'EOF'
7475
# Compute fragment lengths (col3 - col2) and sort/count them in one pipeline
7576
r = bash("awk '{print $3-$2}' swipe.bed | sort -n | uniq -c | awk '{print $2, $1}'")
7677
print("fragment_len count")
7778
print(r["stdout"])
7879
EOF
79-
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_pipeline.py
80+
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_pipeline.py
8081

8182
```
8283

@@ -91,7 +92,7 @@ fragment_len count
9192
The FASTA file is also in the allowed directory. We can count sequences, check contig names, and compute per-base composition without loading the whole file into Python.
9293

9394
```bash
94-
cat > /tmp/bash_fasta.py << 'EOF'
95+
cat > /tmp/bash-demo/bash_fasta.py << 'EOF'
9596
# Count sequences
9697
r = bash("grep -c '^>' swipe.fasta")
9798
print("sequences: " + r["stdout"].strip())
@@ -104,7 +105,7 @@ print("headers:\n" + r["stdout"].strip())
104105
r = bash("grep -v '^>' swipe.fasta | tr -d '\\n' | fold -w1 | sort | uniq -c | sort -rn")
105106
print("base counts:\n" + r["stdout"])
106107
EOF
107-
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_fasta.py
108+
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_fasta.py
108109

109110
```
110111

@@ -126,7 +127,7 @@ base counts:
126127
`bash()` can only write inside the `ai_chat_temp_files/` subdirectory of the allowed directory. Redirects to any other path fail with a read-only filesystem error. Files written there persist to disk and can be read back with `read_file()` or another `bash()` call.
127128

128129
```bash
129-
cat > /tmp/bash_write.py << 'EOF'
130+
cat > /tmp/bash-demo/bash_write.py << 'EOF'
130131
# Sort the BED file by start position and save it
131132
r = bash("sort -k2,2n swipe.bed > ai_chat_temp_files/swipe_sorted.bed")
132133
print("write exit_code: " + str(r["exit_code"]))
@@ -135,7 +136,7 @@ print("write exit_code: " + str(r["exit_code"]))
135136
r2 = bash("head -4 ai_chat_temp_files/swipe_sorted.bed")
136137
print("first 4 lines of sorted BED:\n" + r2["stdout"])
137138
EOF
138-
rm -rf ./demo/ai_chat_temp_files && node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_write.py
139+
rm -rf ./demo/ai_chat_temp_files && node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_write.py
139140

140141
```
141142

@@ -152,7 +153,7 @@ contig_00001 12635 37635 1.a4652b11-87e8-4eb5-8d06-db06b43fa5a3 1 -
152153
Writes outside `ai_chat_temp_files/` fail with EROFS (read-only filesystem).
153154

154155
```bash
155-
cat > /tmp/bash_write_denied.py << 'EOF'
156+
cat > /tmp/bash-demo/bash_write_denied.py << 'EOF'
156157
# Try to write outside ai_chat_temp_files/ — the filesystem error propagates
157158
# as a RuntimeError from bash() itself
158159
try:
@@ -164,7 +165,7 @@ except Exception as e:
164165
print("caught: " + str(type(e).__name__))
165166
print("message: " + msg.split(", write '")[0])
166167
EOF
167-
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_write_denied.py
168+
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_write_denied.py
168169

169170
```
170171

@@ -178,7 +179,7 @@ message: EROFS: read-only file system
178179
A command that exits with a non-zero code does not raise an exception — the exit code is returned inside the dict. This lets scripts distinguish between an empty result and an actual error.
179180

180181
```bash
181-
cat > /tmp/bash_exitcodes.py << 'EOF'
182+
cat > /tmp/bash-demo/bash_exitcodes.py << 'EOF'
182183
# grep returns 1 when no match is found — not an exception
183184
r = bash("grep 'contig_99999' swipe.bed")
184185
print("no-match exit_code: " + str(r["exit_code"]))
@@ -189,7 +190,7 @@ r2 = bash("cat nonexistent_file.txt")
189190
print("missing-file exit_code: " + str(r2["exit_code"]))
190191
print("stderr: " + r2["stderr"].strip())
191192
EOF
192-
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_exitcodes.py
193+
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_exitcodes.py
193194

194195
```
195196

@@ -205,13 +206,13 @@ stderr: cat: nonexistent_file.txt: No such file or directory
205206
Bioinformatics tools and language runtimes are not available. Attempting to call them returns a non-zero exit code with `command not found` in stderr. The available set is: grep, sed, awk, sort, uniq, wc, cut, head, tail, cat, find, tr, paste, jq, and standard bash builtins.
206207

207208
```bash
208-
cat > /tmp/bash_unavailable.py << 'EOF'
209+
cat > /tmp/bash-demo/bash_unavailable.py << 'EOF'
209210
for cmd in ["samtools view demo.bam", "python3 --version", "bedtools --version"]:
210211
r = bash(cmd)
211212
label = cmd.split()[0]
212213
print(label + ": exit_code=" + str(r["exit_code"]) + " stderr=" + repr(r["stderr"].strip()[:50]))
213214
EOF
214-
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_unavailable.py
215+
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_unavailable.py
215216

216217
```
217218

@@ -226,7 +227,7 @@ bedtools: exit_code=127 stderr='bash: bedtools: command not found'
226227
`bash()` is most useful when paired with the BAM query functions. Here we fetch all read lengths from the BAM with `read_info()`, write them to `ai_chat_temp_files/` as a TSV, then use `bash()` to compute summary statistics and find the top-5 longest reads.
227228

228229
```bash
229-
cat > /tmp/bash_combined.py << 'EOF'
230+
cat > /tmp/bash-demo/bash_combined.py << 'EOF'
230231
# Fetch all read lengths from the BAM and write as TSV for bash processing
231232
rows = read_info("demo.bam", limit=200000)
232233
lines = ["read_id\tlength"]
@@ -244,7 +245,7 @@ print("length stats: " + r["stdout"].strip())
244245
r2 = bash("tail -n +2 ai_chat_temp_files/reads.tsv | sort -k2,2rn | head -5 | awk '{print $2, $1}'")
245246
print("top-5 by length (len read_id):\n" + r2["stdout"])
246247
EOF
247-
rm -rf ./demo/ai_chat_temp_files && node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_combined.py
248+
rm -rf ./demo/ai_chat_temp_files && node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_combined.py
248249

249250
```
250251

@@ -265,11 +266,12 @@ top-5 by length (len read_id):
265266
`bash()` is well-suited for summarising data already written to `ai_chat_temp_files/`. Here we compute a read-length histogram from the TSV written in the previous section and render it as a text bar chart entirely in shell using awk and sort.
266267

267268
```bash
268-
cat > /tmp/bash_histogram.py << 'EOF'
269+
cat > /tmp/bash-demo/bash_histogram.py << 'EOF'
269270
r = bash("tail -n +2 ai_chat_temp_files/reads.tsv | awk '{bin=int($2/200)*200; count[bin]++; total++} END{for(k in count){pct=count[k]*100/total; bar=\"\"; for(j=0;j<int(pct/2+0.5);j++) bar=bar \"#\"; printf \"%d-%d bp %3d %s\\n\", k, k+199, count[k], bar}}' | sort -n")
270271
print(r["stdout"])
271272
EOF
272-
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash_histogram.py
273+
node ./dist/execute-cli.mjs --dir ./demo /tmp/bash-demo/bash_histogram.py
274+
rm -rf /tmp/bash-demo
273275

274276
```
275277

0 commit comments

Comments
 (0)