0
# History Management
1
2
Comprehensive history management with programmatic control over command history storage, retrieval, and persistence. The gnureadline history system maintains compatibility with GNU Readline's indexing conventions.
3
4
## Capabilities
5
6
### History Buffer Operations
7
8
Basic operations for adding, retrieving, and manipulating history entries in memory.
9
10
```python { .api }
11
def add_history(line: str):
12
"""
13
Add a line to the history buffer.
14
15
Parameters:
16
- line: Command line to add to history
17
"""
18
19
def clear_history():
20
"""Clear the entire history list."""
21
22
def get_history_item(index: int) -> str:
23
"""
24
Get history item by index (1-based).
25
26
Parameters:
27
- index: History item index (1-based, 0 returns None)
28
29
Returns:
30
str or None: History item or None if index is 0 or out of range
31
"""
32
33
def get_current_history_length() -> int:
34
"""
35
Get the current number of history entries.
36
37
Returns:
38
int: Number of history entries
39
"""
40
```
41
42
### Usage Examples
43
44
```python
45
import gnureadline
46
47
# Build a command history
48
commands = ["ls -la", "cd /home", "python script.py", "git status"]
49
for cmd in commands:
50
gnureadline.add_history(cmd)
51
52
print(f"History length: {gnureadline.get_current_history_length()}") # 4
53
54
# Retrieve history items (1-based indexing)
55
first_cmd = gnureadline.get_history_item(1) # "ls -la"
56
last_cmd = gnureadline.get_history_item(4) # "git status"
57
invalid = gnureadline.get_history_item(0) # None
58
59
# Clear all history
60
gnureadline.clear_history()
61
print(f"After clear: {gnureadline.get_current_history_length()}") # 0
62
```
63
64
### History Modification
65
66
Advanced operations for modifying existing history entries using 0-based indexing.
67
68
```python { .api }
69
def remove_history_item(index: int) -> str:
70
"""
71
Remove history entry by index (0-based).
72
73
Parameters:
74
- index: History item index (0-based)
75
76
Returns:
77
str: Removed history item
78
"""
79
80
def replace_history_item(index: int, line: str) -> str:
81
"""
82
Replace history entry at index (0-based).
83
84
Parameters:
85
- index: History item index (0-based)
86
- line: New history line
87
88
Returns:
89
str: Previous history item at that index
90
"""
91
```
92
93
### Usage Examples
94
95
```python
96
import gnureadline
97
98
# Set up some history
99
gnureadline.add_history("command1")
100
gnureadline.add_history("command2")
101
gnureadline.add_history("command3")
102
103
# Replace second command (0-based index 1)
104
old_cmd = gnureadline.replace_history_item(1, "modified_command2")
105
print(f"Replaced '{old_cmd}' with 'modified_command2'")
106
107
# Remove first command (0-based index 0)
108
removed = gnureadline.remove_history_item(0)
109
print(f"Removed: '{removed}'")
110
111
# Check final state
112
for i in range(gnureadline.get_current_history_length()):
113
print(f"History[{i}]: {gnureadline.get_history_item(i+1)}") # +1 for 1-based get
114
```
115
116
### History Configuration
117
118
Control automatic history behavior and size limits.
119
120
```python { .api }
121
def set_history_length(length: int):
122
"""
123
Set maximum history length.
124
125
Parameters:
126
- length: Maximum number of history entries (-1 for unlimited)
127
"""
128
129
def get_history_length() -> int:
130
"""
131
Get maximum history length setting.
132
133
Returns:
134
int: Maximum history length (-1 if unlimited)
135
"""
136
137
def set_auto_history(enabled: bool):
138
"""
139
Enable or disable automatic history addition.
140
141
Parameters:
142
- enabled: Whether to automatically add lines to history
143
"""
144
```
145
146
### Usage Examples
147
148
```python
149
import gnureadline
150
151
# Set history limit
152
gnureadline.set_history_length(100)
153
print(f"History limit: {gnureadline.get_history_length()}") # 100
154
155
# Disable automatic history (manual control)
156
gnureadline.set_auto_history(False)
157
158
# Unlimited history
159
gnureadline.set_history_length(-1)
160
print(f"Unlimited history: {gnureadline.get_history_length()}") # -1
161
```
162
163
## File Persistence
164
165
### History File Operations
166
167
Save and load history to/from files for persistence across sessions.
168
169
```python { .api }
170
def read_history_file(filename: str = None):
171
"""
172
Load a readline history file.
173
174
Parameters:
175
- filename: History file path (default: ~/.history)
176
"""
177
178
def write_history_file(filename: str = None):
179
"""
180
Save the history to a file.
181
182
Parameters:
183
- filename: History file path (default: ~/.history)
184
"""
185
186
def append_history_file(nelements: int, filename: str = None):
187
"""
188
Append recent history entries to a file.
189
190
Parameters:
191
- nelements: Number of recent entries to append
192
- filename: History file path (default: ~/.history)
193
194
Note: Only available if HAVE_RL_APPEND_HISTORY is defined
195
"""
196
```
197
198
### Usage Examples
199
200
```python
201
import gnureadline
202
import os
203
204
# Set up custom history file location
205
history_file = os.path.expanduser("~/.my_app_history")
206
207
# Load existing history on startup
208
try:
209
gnureadline.read_history_file(history_file)
210
print(f"Loaded {gnureadline.get_current_history_length()} history entries")
211
except OSError as e:
212
print(f"No existing history file: {e}")
213
214
# Add some commands during session
215
gnureadline.add_history("command 1")
216
gnureadline.add_history("command 2")
217
218
# Save complete history on exit
219
try:
220
gnureadline.write_history_file(history_file)
221
print("History saved successfully")
222
except OSError as e:
223
print(f"Failed to save history: {e}")
224
225
# Alternatively, append only recent entries
226
try:
227
gnureadline.append_history_file(10, history_file) # Last 10 entries
228
print("Recent history appended")
229
except OSError as e:
230
print(f"Failed to append history: {e}")
231
```
232
233
## Advanced History Management
234
235
### History Filtering and Processing
236
237
```python
238
import gnureadline
239
import re
240
241
class HistoryManager:
242
def __init__(self, max_length=1000, filter_duplicates=True):
243
self.max_length = max_length
244
self.filter_duplicates = filter_duplicates
245
gnureadline.set_history_length(max_length)
246
gnureadline.set_auto_history(False) # Manual control
247
248
def add_filtered_history(self, line: str):
249
"""Add history with filtering logic."""
250
# Skip empty lines and whitespace-only lines
251
if not line.strip():
252
return
253
254
# Skip commands starting with space (privacy feature)
255
if line.startswith(' '):
256
return
257
258
# Filter out sensitive commands
259
sensitive_patterns = [r'password', r'secret', r'key\s*=']
260
if any(re.search(pattern, line, re.I) for pattern in sensitive_patterns):
261
return
262
263
# Remove duplicates if enabled
264
if self.filter_duplicates:
265
current_length = gnureadline.get_current_history_length()
266
if current_length > 0:
267
last_item = gnureadline.get_history_item(current_length)
268
if last_item == line:
269
return
270
271
gnureadline.add_history(line)
272
273
def search_history(self, pattern: str) -> list:
274
"""Search history for matching entries."""
275
matches = []
276
length = gnureadline.get_current_history_length()
277
278
for i in range(1, length + 1):
279
item = gnureadline.get_history_item(i)
280
if item and re.search(pattern, item, re.I):
281
matches.append((i, item))
282
283
return matches
284
285
def cleanup_history(self):
286
"""Remove old/redundant entries to keep history manageable."""
287
length = gnureadline.get_current_history_length()
288
if length <= self.max_length:
289
return
290
291
# Keep only the most recent entries
292
keep_entries = []
293
start_index = length - self.max_length + 1
294
295
for i in range(start_index, length + 1):
296
item = gnureadline.get_history_item(i)
297
if item:
298
keep_entries.append(item)
299
300
# Clear and rebuild history
301
gnureadline.clear_history()
302
for entry in keep_entries:
303
gnureadline.add_history(entry)
304
305
# Usage example
306
history_mgr = HistoryManager(max_length=500, filter_duplicates=True)
307
308
# Add commands with filtering
309
commands = [
310
"ls -la",
311
"cd /home",
312
"ls -la", # Duplicate - will be filtered
313
" secret_command", # Starts with space - will be filtered
314
"export PASSWORD=secret", # Contains sensitive data - will be filtered
315
"python script.py"
316
]
317
318
for cmd in commands:
319
history_mgr.add_filtered_history(cmd)
320
321
# Search history
322
matches = history_mgr.search_history("python")
323
print(f"Found {len(matches)} matches for 'python'")
324
325
# Periodic cleanup
326
history_mgr.cleanup_history()
327
```
328
329
## Index Compatibility Notes
330
331
**Important**: GNU Readline uses different indexing conventions for different operations:
332
333
- `get_history_item()`: **1-based indexing** (index 0 returns None)
334
- `remove_history_item()` and `replace_history_item()`: **0-based indexing**
335
336
This matches the GNU Readline C library behavior for compatibility with existing applications.
337
338
```python
339
import gnureadline
340
341
# Add some test data
342
gnureadline.add_history("first") # Will be at index 0 for remove/replace
343
gnureadline.add_history("second") # Will be at index 1 for remove/replace
344
345
# Getting items (1-based)
346
first = gnureadline.get_history_item(1) # "first"
347
second = gnureadline.get_history_item(2) # "second"
348
349
# Modifying items (0-based)
350
gnureadline.replace_history_item(0, "FIRST") # Replace "first" with "FIRST"
351
gnureadline.remove_history_item(1) # Remove "second"
352
353
# Verify the change
354
print(gnureadline.get_history_item(1)) # "FIRST"
355
print(gnureadline.get_history_item(2)) # None (removed)
356
```