1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
|
(pane-interaction)=
# Pane Interaction
libtmux provides powerful methods for interacting with tmux panes programmatically.
This is especially useful for automation, testing, and orchestrating terminal-based
workflows.
Open two terminals:
Terminal one: start tmux in a separate terminal:
```console
$ tmux
```
Terminal two, `python` or `ptpython` if you have it:
```console
$ python
```
## Sending Commands
The {meth}`~libtmux.Pane.send_keys` method sends text to a pane, optionally pressing
Enter to execute it.
### Basic command execution
```python
>>> pane = window.split(shell='sh')
>>> pane.send_keys('echo "Hello from libtmux"')
>>> import time; time.sleep(0.1) # Allow command to execute
>>> output = pane.capture_pane()
>>> 'Hello from libtmux' in '\\n'.join(output)
True
```
### Send without pressing Enter
Use `enter=False` to type text without executing:
```python
>>> pane.send_keys('echo "waiting"', enter=False)
>>> # Text is typed but not executed
>>> output = pane.capture_pane()
>>> 'waiting' in '\\n'.join(output)
True
```
Press Enter separately with {meth}`~libtmux.Pane.enter`:
```python
>>> import time
>>> # First type something without pressing Enter
>>> pane.send_keys('echo "execute me"', enter=False)
>>> pane.enter() # doctest: +ELLIPSIS
Pane(%... Window(@... ..., Session($... ...)))
>>> time.sleep(0.2)
>>> output = pane.capture_pane()
>>> 'execute me' in '\\n'.join(output)
True
```
### Literal mode for special characters
Use `literal=True` to send special characters without interpretation:
```python
>>> import time
>>> pane.send_keys('echo "Tab:\\tNewline:\\n"', literal=True)
>>> time.sleep(0.1)
```
### Suppress shell history
Use `suppress_history=True` to prepend a space (prevents command from being
saved in shell history):
```python
>>> import time
>>> pane.send_keys('echo "secret command"', suppress_history=True)
>>> time.sleep(0.1)
```
## Capturing Output
The {meth}`~libtmux.Pane.capture_pane` method captures text from a pane's buffer.
### Basic capture
```python
>>> import time
>>> pane.send_keys('echo "Line 1"; echo "Line 2"; echo "Line 3"')
>>> time.sleep(0.1)
>>> output = pane.capture_pane()
>>> isinstance(output, list)
True
>>> any('Line 2' in line for line in output)
True
```
### Capture with line ranges
Capture specific line ranges using `start` and `end` parameters:
```python
>>> # Capture last 5 lines of visible pane
>>> recent = pane.capture_pane(start=-5, end='-')
>>> isinstance(recent, list)
True
>>> # Capture from start of history to current
>>> full_history = pane.capture_pane(start='-', end='-')
>>> len(full_history) >= 0
True
```
## Waiting for Output
A common pattern in automation is waiting for a command to complete.
### Polling for completion marker
```python
>>> import time
>>> pane.send_keys('sleep 0.2; echo "TASK_COMPLETE"')
>>> # Poll for completion
>>> for _ in range(30):
... output = pane.capture_pane()
... if 'TASK_COMPLETE' in '\\n'.join(output):
... break
... time.sleep(0.1)
>>> 'TASK_COMPLETE' in '\\n'.join(output)
True
```
### Helper function for waiting
```python
>>> import time
>>> def wait_for_text(pane, text, timeout=5.0):
... """Wait for text to appear in pane output."""
... start = time.time()
... while time.time() - start < timeout:
... output = pane.capture_pane()
... if text in '\\n'.join(output):
... return True
... time.sleep(0.1)
... return False
>>> pane.send_keys('echo "READY"')
>>> wait_for_text(pane, 'READY', timeout=2.0)
True
```
## Querying Pane State
The {meth}`~libtmux.Pane.display_message` method queries tmux format variables.
### Get pane dimensions
```python
>>> width = pane.display_message('#{pane_width}', get_text=True)
>>> isinstance(width, list) and len(width) > 0
True
>>> height = pane.display_message('#{pane_height}', get_text=True)
>>> isinstance(height, list) and len(height) > 0
True
```
### Get pane information
```python
>>> # Current working directory
>>> cwd = pane.display_message('#{pane_current_path}', get_text=True)
>>> isinstance(cwd, list)
True
>>> # Pane ID
>>> pane_id = pane.display_message('#{pane_id}', get_text=True)
>>> pane_id[0].startswith('%')
True
```
### Common format variables
| Variable | Description |
|----------|-------------|
| `#{pane_width}` | Pane width in characters |
| `#{pane_height}` | Pane height in characters |
| `#{pane_current_path}` | Current working directory |
| `#{pane_pid}` | PID of the pane's shell |
| `#{pane_id}` | Unique pane ID (e.g., `%0`) |
| `#{pane_index}` | Pane index in window |
## Resizing Panes
The {meth}`~libtmux.Pane.resize` method adjusts pane dimensions.
### Resize by specific dimensions
```python
>>> # Make pane larger
>>> pane.resize(height=20, width=80) # doctest: +ELLIPSIS
Pane(%... Window(@... ..., Session($... ...)))
```
### Resize by adjustment
```python
>>> from libtmux.constants import ResizeAdjustmentDirection
>>> # Increase height by 5 rows
>>> pane.resize(adjustment_direction=ResizeAdjustmentDirection.Up, adjustment=5) # doctest: +ELLIPSIS
Pane(%... Window(@... ..., Session($... ...)))
>>> # Decrease width by 10 columns
>>> pane.resize(adjustment_direction=ResizeAdjustmentDirection.Left, adjustment=10) # doctest: +ELLIPSIS
Pane(%... Window(@... ..., Session($... ...)))
```
### Zoom toggle
```python
>>> # Zoom pane to fill window
>>> pane.resize(zoom=True) # doctest: +ELLIPSIS
Pane(%... Window(@... ..., Session($... ...)))
>>> # Unzoom
>>> pane.resize(zoom=True) # doctest: +ELLIPSIS
Pane(%... Window(@... ..., Session($... ...)))
```
## Clearing the Pane
The {meth}`~libtmux.Pane.clear` method clears the pane's screen:
```python
>>> pane.clear() # doctest: +ELLIPSIS
Pane(%... Window(@... ..., Session($... ...)))
```
## Killing Panes
The {meth}`~libtmux.Pane.kill` method destroys a pane:
```python
>>> # Create a temporary pane
>>> temp_pane = pane.split()
>>> temp_pane in window.panes
True
>>> # Kill it
>>> temp_pane.kill()
>>> temp_pane not in window.panes
True
```
### Kill all except current
```python
>>> # Setup: create multiple panes
>>> pane.window.resize(height=60, width=120) # doctest: +ELLIPSIS
Window(@... ...)
>>> keep_pane = pane.split()
>>> extra1 = pane.split()
>>> extra2 = pane.split()
>>> # Kill all except keep_pane
>>> keep_pane.kill(all_except=True)
>>> keep_pane in window.panes
True
>>> extra1 not in window.panes
True
>>> extra2 not in window.panes
True
>>> # Cleanup
>>> keep_pane.kill()
```
## Practical Recipes
### Recipe: Run command and capture output
```python
>>> import time
>>> def run_and_capture(pane, command, marker='__DONE__', timeout=5.0):
... """Run a command and return its output."""
... pane.send_keys(f'{command}; echo {marker}')
... start = time.time()
... while time.time() - start < timeout:
... output = pane.capture_pane()
... output_str = '\\n'.join(output)
... if marker in output_str:
... return output # Return all captured output
... time.sleep(0.1)
... raise TimeoutError(f'Command did not complete within {timeout}s')
>>> result = run_and_capture(pane, 'echo "captured text"', timeout=2.0)
>>> 'captured text' in '\\n'.join(result)
True
```
### Recipe: Check for error patterns
```python
>>> import time
>>> def check_for_errors(pane, error_patterns=None):
... """Check pane output for error patterns."""
... if error_patterns is None:
... error_patterns = ['error:', 'Error:', 'ERROR', 'failed', 'FAILED']
... output = '\\n'.join(pane.capture_pane())
... for pattern in error_patterns:
... if pattern in output:
... return True
... return False
>>> pane.send_keys('echo "All good"')
>>> time.sleep(0.1)
>>> check_for_errors(pane)
False
```
:::{seealso}
- {ref}`api` for the full API reference
- {class}`~libtmux.Pane` for all pane methods
- {ref}`automation-patterns` for advanced orchestration patterns
:::
|