2013-05-30 12 views
5

Pracuję nad utworzeniem narzędzia wiersza polecenia za pomocą Cmd.cmd z Pythona i chcę dodać polecenie "load" z argumentem filename, co wspiera zakładkę ukończenie.Ulepszanie nazwy pliku w Cmd.cmd z Pythona

Odnosząc this i this, ja szalony kod tak:

import os, cmd, sys, yaml 
import os.path as op 
import glob as gb 

def _complete_path(path): 
    if op.isdir(path): 
     return gb.glob(op.join(path, '*')) 
    else: 
     return gb.glob(path+'*') 

class CmdHandler(cmd.Cmd): 

    def do_load(self, filename): 
     try: 
      with open(filename, 'r') as f: 
       self.cfg = yaml.load(f) 
     except: 
      print 'fail to load the file "{:}"'.format(filename) 

    def complete_load(self, text, line, start_idx, end_idx): 
     return _complete_path(text) 

Działa to dobrze dla CWD jednak, gdy chcę, aby przejść do podkatalogu, po subdir/Potem "tekst" funkcji complete_load staje się pusty, więc _complete_path func ponownie zwraca cwd.

Nie wiem, jak uzyskać zawartość podkatalogu z uzupełnianiem tab. Proszę o pomoc!

+0

trudno mi uwierzyć, że nikt nie odpowiedział na ten –

Odpowiedz

1

Nie sądzę, jest to najlepsza odpowiedź, ale mam funkcję co zamierzam za słuszne:

def _complete_path(text, line): 
    arg = line.split()[1:] 
    dir, base = '', '' 
    try: 
     dir, base = op.split(arg[-1]) 
    except: 
     pass 
    cwd = os.getcwd() 
    try: 
     os.chdir(dir) 
    except: 
     pass 
    ret = [f+os.sep if op.isdir(f) else f for f in os.listdir('.') if f.startswith(base)] 
    if base == '' or base == '.': 
     ret.extend(['./', '../']) 
    elif base == '..': 
     ret.append('../') 
    os.chdir(cwd) 
    return ret 

    ............................. 

    def complete_load(self, text, line, start_idx, end_idx): 
     return _complete_path(text, line) 

nie używałem „text” z complete_cmd(), ale wykorzystanie argument parsujący "wiersz" bezpośrednio. Jeśli masz lepszy pomysł, proszę dać mi znać.

0

Mam taki sam pomysł z jinserkiem, ale w inny sposób. Oto mój kod:

def complete_load(self, text, line, begidx, endidx): 
    arg = line.split()[1:] 

    if not arg: 
     completions = os.listdir('./') 
    else: 
     dir, part, base = arg[-1].rpartition('/') 
     if part == '': 
      dir = './' 
     elif dir == '': 
      dir = '/'    

     completions = [] 
     for f in os.listdir(dir): 
      if f.startswith(base): 
       if os.path.isfile(os.path.join(dir,f)): 
        completions.append(f) 
       else: 
        completions.append(f+'/') 

    return completions 

proszę dać mi znać, jeśli masz lepszy pomysł. Uwaga: Myślę, że ta metoda działa tylko w systemie operacyjnym rodziny Unix, ponieważ tworzę ten kod na podstawie struktury katalogów Unix.

3

Wdrażanie dokończenie nazw plików z cmd jest nieco kłopotliwe, ponieważ bazowy readline biblioteka interpretuje znaki specjalne takie jak „/” i „-” (i inni) jako separatory, a to ustawia który podciąg ciągu linii jest zostać zastąpione przez uzupełnienia.

Na przykład

> load /hom<tab> 

rozmowy complete_load() z

text='hom', line='load /hom', begidx=6, endidx=9 
text is line[begidx:endidx] 

'tekst' nie "/ hom", ponieważ biblioteki readline analizowana linia i zwraca ciąg znaków po '/"separator. Funkcja complete_load() powinna zwrócić listę ciągów ukończenia, które zaczynają się od "hom", a nie "/ hom", ponieważ uzupełnienia zastąpią podciąg rozpoczynający się na początku łańcucha. Jeśli funkcja complete_load() niepoprawnie zwraca [ '/ home'], linia staje

> load //home 

który nie jest dobrym rozwiązaniem.

Pozostałe znaki są uważane za separatory za pomocą readline, a nie tylko ukośników, , więc nie można przejąć podciągu, zanim "text" będzie katalogiem nadrzędnym.Dla przykład:

> load /home/mike/my-file<tab> 

rozmowy complete_load() z

text='file', line='load /home/mike/my-file', begidx=19, endidx=23 

Zakładając/home/mike zawiera pliki my-plik1 oraz my-file2 Z uzupełnienia powinny być [ 'plik1', ' file2 '], nie [' my-plik1 ',' mój-plik2 '], ani ['/home/mike/my-file1 ','/home/mike/my-file2 ']. Jeśli zwrócisz pełne ścieżki, wynikiem będzie:

> load /home/mike/my-file/home/mike/my-file1 

Podejście, którego użyłem, polegało na użyciu modułu globu w celu znalezienia pełnych ścieżek. Glob działa dla ścieżek bezwzględnych i ścieżek względnych. Po znalezieniu ścieżek usuwam "stałą" część, która jest podciągana przed łańcuchem znaków.

Najpierw należy przeanalizować stały argument, który jest podłańcem między spacją a begidx.

index = line.rindex(' ', 0, begidx) # -1 if not found 
fixed = line[index + 1: begidx] 

Argument jest między spacją a końcem linii. Dołącz gwiazdkę, aby utworzyć globalny wzór wyszukiwania.

I dołączania a „/” do wyników, które są katalogi, a to sprawia, że ​​łatwiej przemierzać katalogi z zakończeniem zakładki (w przeciwnym razie trzeba nacisnąć klawisz tabulatora dwukrotnie dla każdego katalogu), a to sprawia, że ​​jest oczywiste, użytkownik, którego elementami uzupełniającymi są katalogi i które są plikami.

Na końcu usuń "ustaloną" część ścieżek, więc readline zastąpi tylko część "tekstową".

import os 
import glob 
import cmd 

def _append_slash_if_dir(p): 
    if p and os.path.isdir(p) and p[-1] != os.sep: 
     return p + os.sep 
    else: 
     return p 

class MyShell(cmd.Cmd): 
    prompt = "> " 

    def do_quit(self, line): 
     return True 

    def do_load(self, line): 
     print("load " + line) 

    def complete_load(self, text, line, begidx, endidx): 
     before_arg = line.rfind(" ", 0, begidx) 
     if before_arg == -1: 
      return # arg not found 

     fixed = line[before_arg+1:begidx] # fixed portion of the arg 
     arg = line[before_arg+1:endidx] 
     pattern = arg + '*' 

     completions = [] 
     for path in glob.glob(pattern): 
      path = _append_slash_if_dir(path) 
      completions.append(path.replace(fixed, "", 1)) 
     return completions 

MyShell().cmdloop() 
+0

przypuszczam kawałek byłby bardziej efektywny niż wezwanie do zastąpienia(). Wystarczy obliczyć indeks dla plasterka, który jest podobny do begidx - before_arg. – meffie

+0

Próbowałem podobnego podejścia, niestety nie obsługuje białych znaków w ścieżkach. shlex.split (linia) nie może pomóc, ponieważ usuwa białe spacje między elementami liniowymi, więc nie można znaleźć odpowiedniego elementu i pozycji w tym elemencie. Naprawdę niefortunne. –

4

Twoim głównym problemem jest to, że biblioteki readline ograniczającej rzeczy w oparciu o to domyślny zestaw separatora:

import readline 
readline.get_completer_delims() 
# yields ' \t\n`[email protected]#$%^&*()-=+[{]}\\|;:\'",<>/?' 

Po ukończeniu zakładka do nazwy pliku mogę usunąć wszystko z tego, ale spacji.

import readline 
readline.set_completer_delims(' \t\n') 

Po ustawieniu separatorów parametr "tekst" do funkcji uzupełniania powinien być bardziej zgodny z oczekiwaniami.

Rozwiązuje to również często występujące problemy z uzupełnianiem kart, powieleniem części tekstu.

0

dokonałem tego wykonując:

def complete_listFolder(self, text, line, begidx, endidx): 
    path = os.path.relpath(os.path.normpath(line.split()[1])) 
      if not os.path.isdir(path) and not os.path.isfile(path): 
       baseName = os.path.basename(path) 
       dirName = os.path.dirname(path) 
       return fnmatch.filter(os.listdir(dirName), baseName + "*") 

      completions = [completion for completion in os.listdir(path)]  
      return completions 

Off Oczywiście jest wiele do poprawy, ale mam nadzieję, że to pomaga.

=)

0

użyć shlex analizowania linii. W przeciwieństwie do niektórych innych rozwiązań obsługuję ścieżki cytowane i sekwencje ze znakami ucieczkowymi (tzn. Ścieżki z białymi znakami), a uzupełnianie działa dla dowolnej pozycji kursora. Nie testowałem zbyt intensywnie, więc przebieg może się różnić.

def path_completion(self, text, line, startidx, endidx): 
    try: 
     glob_prefix = line[:endidx] 

     # add a closing quote if necessary 
     quote = ['', '"', "'"] 
     while len(quote) > 0: 
      try: 
       split = [s for s in shlex.split(glob_prefix + quote[0]) if s.strip()] 
      except ValueError as ex: 
       assert str(ex) == 'No closing quotation', 'Unexpected shlex error' 
       quote = quote[1:] 
      else: 
       break 
     assert len(quote) > 0, 'Could not find closing quotation' 

     # select relevant line segment 
     glob_prefix = split[-1] if len(split) > 1 else '' 

     # expand tilde 
     glob_prefix = os.path.expanduser(glob_prefix) 

     # find matches 
     matches = glob.glob(glob_prefix + '*') 

     # append os.sep to directories 
     matches = [match + os.sep if Path(match).is_dir() else match for match in matches] 

     # cutoff prefixes 
     cutoff_idx = len(glob_prefix) - len(text) 
     matches = [match[cutoff_idx:] for match in matches] 

     return matches 
    except: 
     traceback.print_exc() 
0

To działa dla mnie.Usuń "self", jeśli nie używasz w klasie.

def _complete_path(self, path): 
    if os.path.isdir(path): 
     return gb.glob(os.path.join(path, '*')) 
    else: 
     return gb.glob(path + '*') 

def complete_load(self, text, line, start_idx, end_idx): 
    mline = line.split(' ')[-1] 
    offs = len(mline) - len(text) 
    completions = [] 
    if line.split()[-2] == '-p': 
     completions = self._complete_path(mline) 
    return [s[offs:] for s in completions if s.startswith(mline)]