copyclip: Copy Jupyter Notebook Cells to macOS Clipboard¶
The copyclip tool is a Bash script designed to extract and format the contents of a Jupyter Notebook (.ipynb) and copy it to the macOS clipboard using pbcopy. It parses notebook cells using Python and converts them to readable Markdown format, which is especially useful for sharing notebook content or documenting it outside of the Jupyter environment.
🛠️ Features¶
- Parses
.ipynbfiles using Python 3 - Outputs Markdown-formatted content
- Differentiates between code and markdown cells
- Automatically copies the output to clipboard on macOS
- Provides clear cell separation and visual structure
📦 Dependencies¶
python3(installed by default on macOS)pbcopy(macOS utility for clipboard access)- A valid
.ipynbfile
📄 Script Source¶
copyclip
#!/bin/bash
# Print usage function
usage() {
echo "Usage: ./copyclip.sh [-h] [--no-output] <path_to_notebook.ipynb>"
echo "Options:"
echo " -h, --help Show this help message and exit"
echo " --no-output Exclude cell outputs from the copied text"
}
# Initialize variables (output is included by default)
WITH_OUTPUT=1
FILE=""
# Parse arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
-h|--help)
usage
exit 0
;;
--no-output)
WITH_OUTPUT=0
shift
;;
*)
if [ -z "$FILE" ]; then
FILE="$1"
fi
shift
;;
esac
done
# Check if a file was provided
if [ -z "$FILE" ]; then
usage
exit 1
fi
# Check if the file exists
if [ ! -f "$FILE" ]; then
echo "Error: File '$FILE' not found."
exit 1
fi
# Use a quoted HEREDOC (<< 'EOF') so Bash ignores the backticks completely
python3 - "$FILE" "$WITH_OUTPUT" << 'EOF' | pbcopy
import sys, json
file_path = sys.argv[1]
with_output = sys.argv[2] == '1'
try:
with open(file_path, 'r', encoding='utf-8') as f:
nb = json.load(f)
output = []
for i, cell in enumerate(nb.get('cells', [])):
cell_type = cell.get('cell_type', '')
source = ''.join(cell.get('source', []))
# Add a visual separator for clarity
output.append(f'--- CELL {i+1} ({cell_type.upper()}) ---')
if cell_type == 'code':
# Wrap code in markdown code blocks
output.append('```python')
output.append(source)
output.append('```')
# Extract output if the flag is enabled (now the default)
if with_output and 'outputs' in cell and cell['outputs']:
output.append('\n**Output:**')
output.append('```text')
for out in cell['outputs']:
out_type = out.get('output_type', '')
if out_type == 'stream':
output.append(''.join(out.get('text', [])))
elif out_type in ('execute_result', 'display_data'):
data = out.get('data', {})
if 'text/plain' in data:
output.append(''.join(data['text/plain']))
elif out_type == 'error':
# Join the traceback for errors
output.append(''.join(out.get('traceback', [])))
output.append('```')
else:
# Markdown cells are added as-is
output.append(source)
output.append('\n')
# Print to stdout so bash can pipe it
print('\n'.join(output))
except Exception as e:
sys.stderr.write(f'Error processing notebook: {e}\n')
sys.exit(1)
EOF
# Check exit status of the python script in the pipeline
if [ ${PIPESTATUS[0]} -eq 0 ]; then
if [ "$WITH_OUTPUT" -eq 1 ]; then
echo "✅ Successfully extracted '$FILE' (with outputs) and copied to clipboard."
else
echo "✅ Successfully extracted '$FILE' (without outputs) and copied to clipboard."
fi
else
echo "❌ Failed to copy to clipboard."
fi