Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   Python (http://www.programmingforums.org/forum43.html)
-   -   Python shell help (http://www.programmingforums.org/showthread.php?t=12001)

bulio Nov 25th, 2006 8:53 PM

Python shell help
 
Hi everyone,

For the fun of it, I am trying to write a shell program similar to Linux's bash or Windows' command. I'm writing the program on Windows, and this is what I have so far:

:

import os

os.chdir('/') #Change the directory to Windows root

while True:
    directory = os.path.abspath(os.curdir)
    directory += '>>'
    prompt = raw_input(directory) #Print the command line prompt as C:\>>


So far, it waits for the user's input by looping C:\>>.

My next step is to actually make the shell respond to user commands. For example, if the user inputs put "Text here" the shell will interpret the statement as print "Text here".

I think the best thing to use is a parser or string methods. The problem is that I haven't found many docs on either item, and am not sure how to implement this. Does anyone here have a tutorial or an example on how I would parse text so that python can interpret it and do what I want? (I also want to do things such as change directory with a command like dir <dir here> and so on.)

Thanks

Darkhack Nov 25th, 2006 9:07 PM

I would read up on parsers or interpreters. Complexity is also a big part of it. You might think of having a tokenizer if you plan to have long or complex commands. Otherwise a simple <command attrib1 attrib2 attrib3> could be done with simple string parsing.

I'm not familiar with Python myself but read the string and seperate each word in an array. Item zero would be your command and the rest would be the attributes passed to it. From there you can use a series of if-statements to determine what functions to use.

titaniumdecoy Nov 25th, 2006 9:11 PM

The str.split function is extremely useful. For example, "slice me up".split() produces the list ['slice', 'me', 'up'].

bulio Nov 25th, 2006 9:44 PM

.split() may work, but how would I use that to determine if the user wrote put or not?

Darkhack Nov 25th, 2006 10:00 PM

This might help. You simply use an if-statement on your array.

http://www.ibiblio.org/g2swap/byteof...statement.html

Sane Nov 25th, 2006 10:10 PM

Quote:

Originally Posted by Darkhack (Post 119732)
This might help. You simply use an if-statement on your array.

http://www.ibiblio.org/g2swap/byteof...statement.html

BURN!!!! Hahahaha.

I'm sure that wasn't actually your question. You should probably specify what you mean by that. Are you talking about case-sensitive circumstances, or situations where there may be other garbage tagged along with it?

Or are you talking about how you could elegantly link each command to a different function call, without repetitive code?

I'd look into the idea of using a dictionary to map each command to a function call:
:

def output (*args):
    print args[0][0]

def list_of_files (*args):
    pass

def run_file (*args):
    pass

# parse the input in to a command and list of parameters
command = 'put'
params = ['"Text Here"']

# turn the command into a function pointer
commands = {"put":output, "dir":list_of_files, "start":run_file}
function = commands[command]

# call the function pointer with the parsed parameters
function (params)


Arevos Nov 26th, 2006 6:06 AM

Quote:

Originally Posted by bulio (Post 119724)
For the fun of it, I am trying to write a shell program similar to Linux's bash or Windows' command.

Funnily enough, every so often I try to do exactly the same thing. Though for my next attempt I think I'll use Haskell.

My advice is to look into a parser generator like PyParsing. You could use this to define a simple grammar:
:

  1. from pyparsing import CharsNotIn, OneOrMore, Empty
  2.  
  3. string  = CharsNotIn(" '\"")
  4. command = OneOrMore(string + Empty())
  5. line    = command | Empty()

This defines a string as consisting of any character apart from a space or a quotation mark. A command (plus arguments) consists of number of strings, and a line can either be a command, or it can be empty.

Aha, you might very well say, but why does it say OneOrMore(string + Empty()), rather than OneOrMore(string)? Unfortunately, I forget the exact reason, as I've lost the email where I quizzed the creator of PyParsing about it. However, IIRC it has to do with conflicts between PyParsing's whitespace handling and any sufficiently broad CharsNotIn - the Empty() object ensures that the grammar is parsed unambiguously.

Anyway, once you have your grammar, you can define some commands to go with it. In Unix, there are functions to resolve the command PATH, but in Windows those functions appear to be absent, so I think we have to create our own. If anyone finds a better solution, please tell me:
:

  1. import os
  2.  
  3. PATHS = ["/bin", "/usr/bin"]
  4.  
  5. def find_command(name):
  6.         for path in PATHS:
  7.                 filepath = os.path.join(path, name)
  8.                 if os.path.isfile(filepath):
  9.                         return filepath
  10.  
  11. def exec_command(string, location, tokens):
  12.         os.spawnv(os.P_WAIT, find_command(tokens[0]), tokens[:])

The find_command function should be pretty obvious. The exec_command function is designed to accept output from PyParsing. It contains three arguments: the substring that was parsed, the location of the substring, and a list of parsed tokens. Usually one is only interested in the tokens, which in this case will be a command plus its arguments.

The exec_command function uses os.spawnv to execute an external command with a set of arguments. Of course, this function could just as easily execute internal functions as well, or indeed do whatever you wish.

The next step is to associate exec_command with the command grammar object:
:

  1. command.setParseAction(exec_command)

Which is simple enough. Then it's a case of constructing a simple command line loop:

:

  1. while True:
  2.         try:
  3.                 input = raw_input(">> ")
  4.         except EOFError:
  5.                 break
  6.         else:
  7.                 line.parseString(input)

One could forgo PyParsing and use string.split instead to get the tokens. However, unlike a simple split function, a parsing system like PyParsing is rather more flexible, allowing you to insert control structures and other more complex functionality with relative ease.


All times are GMT -5. The time now is 12:40 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC