我知道我可以这样做,以确保在Python中获得制表符完成的效果。
import readline
COMMANDS = ['extra', 'extension', 'stuff', 'errors',
'email', 'foobar', 'foo']
def complete(text, state):
for cmd in COMMANDS:
if cmd.startswith(text):
if not state:
return cmd
else:
state -= 1
readline.parse_and_bind("tab: complete")
readline.set_completer(complete)
raw_input('Enter section name: ')
我现在对使用目录进行制表符补全感兴趣。 (/home/user/doc >选项卡)
我将如何去做这样的任务?
以下是如何执行文件系统路径增量补全的快速示例。我修改了您的示例,将其组织到一个类中,其中名为
complete_[name]
的方法指示顶级命令。
我将完成函数切换为使用内部读取行缓冲区来确定整体完成的状态,这使得状态逻辑更简单一些。路径完成位于
_complete_path(path)
方法中,并且我已连接 extra 命令来对其参数执行路径完成。
我确信代码可以进一步简化,但它应该为您提供一个不错的起点:
import os
import re
import readline
COMMANDS = ['extra', 'extension', 'stuff', 'errors',
'email', 'foobar', 'foo']
RE_SPACE = re.compile('.*\s+$', re.M)
class Completer(object):
def _listdir(self, root):
"List directory 'root' appending the path separator to subdirs."
res = []
for name in os.listdir(root):
path = os.path.join(root, name)
if os.path.isdir(path):
name += os.sep
res.append(name)
return res
def _complete_path(self, path=None):
"Perform completion of filesystem path."
if not path:
return self._listdir('.')
dirname, rest = os.path.split(path)
tmp = dirname if dirname else '.'
res = [os.path.join(dirname, p)
for p in self._listdir(tmp) if p.startswith(rest)]
# more than one match, or single match which does not exist (typo)
if len(res) > 1 or not os.path.exists(path):
return res
# resolved to a single directory, so return list of files below it
if os.path.isdir(path):
return [os.path.join(path, p) for p in self._listdir(path)]
# exact file match terminates this completion
return [path + ' ']
def complete_extra(self, args):
"Completions for the 'extra' command."
if not args:
return self._complete_path('.')
# treat the last arg as a path and complete it
return self._complete_path(args[-1])
def complete(self, text, state):
"Generic readline completion entry point."
buffer = readline.get_line_buffer()
line = readline.get_line_buffer().split()
# show all commands
if not line:
return [c + ' ' for c in COMMANDS][state]
# account for last argument ending in a space
if RE_SPACE.match(buffer):
line.append('')
# resolve command to the implementation function
cmd = line[0].strip()
if cmd in COMMANDS:
impl = getattr(self, 'complete_%s' % cmd)
args = line[1:]
if args:
return (impl(args) + [None])[state]
return [cmd + ' '][state]
results = [c + ' ' for c in COMMANDS if c.startswith(cmd)] + [None]
return results[state]
comp = Completer()
# we want to treat '/' as part of a word, so override the delimiters
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(comp.complete)
raw_input('Enter section name: ')
用途:
% python complete.py
Enter section name: ext<tab>
extension extra
Enter section name: extra foo<tab>
foo.py foo.txt foo/
Enter section name: extra foo/<tab>
foo/bar.txt foo/baz.txt
Enter section name: extra foo/bar.txt
更新 如果用户输入
/
:,它将从根开始完成路径
% python complete.py
Enter section name: extra /Use<tab>
/Users/.localized /Users/Shared/ /Users/user1 /Users/user2
Enter section name: extra /Users/use<tab>
/Users/user1 /Users/user2
这足以使用 raw_input() 启用内置目录选项卡补全:
import readline
readline.parse_and_bind("tab: complete")
这个版本适用于python3,使用pathlib,并且是tab完成文件/目录的简约版本。它基于上面的一些答案,但仅适用于文件/目录。
#!/usr/bin/python
import pathlib
import readline
def complete_path(text, state):
incomplete_path = pathlib.Path(text)
if incomplete_path.is_dir():
completions = [p.as_posix() for p in incomplete_path.iterdir()]
elif incomplete_path.exists():
completions = [incomplete_path]
else:
exists_parts = pathlib.Path('.')
for part in incomplete_path.parts:
test_next_part = exists_parts / part
if test_next_part.exists():
exists_parts = test_next_part
completions = []
for p in exists_parts.iterdir():
p_str = p.as_posix()
if p_str.startswith(text):
completions.append(p_str)
return completions[state]
# we want to treat '/' as part of a word, so override the delimiters
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(complete_path)
print(input('tab complete a filename: '))
用于路径完成
import os
import sys
import readline
import glob
def path_completer(text, state):
"""
This is the tab completer for systems paths.
Only tested on *nix systems
"""
line = readline.get_line_buffer().split()
if '~' in text:
text = os.path.expanduser('~')
return [x for x in glob.glob(text+'*')][state]
if __name__=="__main__":
readline.set_completer_delims('\t')
readline.parse_and_bind("tab: complete")
readline.set_completer(path_completer)
ans = input("What file do you want? ")
print(ans)
请注意,我已经改进了在 https://gist.github.com/iamatypeofwalrus/5637895
找到的代码在某些系统™上,您需要使用不同的绑定。例如,我没有 GNU readline,所以我需要使用不同的 parse_and_bind 文本:
if 'libedit' in readline.__doc__:
readline.parse_and_bind("bind ^I rl_complete")
else:
readline.parse_and_bind("tab: complete")
已经晚了,但为了完整性而发布解决方案。在 Python 3.11 中,以下代码适用于目录和文件选择。
import readline
readline.set_completer_delims(" \t\n;")
readline.parse_and_bind("tab: complete")
ip = input("Enter some text or hit tab for directory/filename: ")
print("You entered: ", ip)