> system "command"
is a start, but is it possible to take stdout from a system call program
as a (string (?)) parameter?
One can put any arbitrary system call into the parameter for system,
right? Including pipes and redirects...
So anything is possible, so long as the stdout/err don't vanish into thin
air!
I've tried doing simple things in perl but its a worry.
Unless someones got suggestions about functional stylings in perl?
>> system "command"
MT> is a start, but is it possible to take stdout from a system call program
MT> as a (string (?)) parameter?
It will be a very non-functional entity, 'cause of state-changing nature
of "system" commands. It will be better to think about COMPLETELY FUNCTIONAL
OPERATING ENVIRONMENT. I know it is possible, but I don't know how can it
be implemented in a current environments.
--
V.S.Lugovsky aka Mauhuur (http://ontil.ihep.su/~vsl) (UIN=45482254)
>> system "command"
I don't know of any explicitly functional attempts in Perl.
But if you want to solve your problem of using the stdout of
a program as a string in Perl, you can use the backtick-operator:
################################################
#!/usr/bin/perl -w
use strict; # strict syntax-checks
my $result_string = `ls`; # store result of "ls"-program in the string
print "\n Hello, the resulting string of \"ls\" is: $result_string \n\n";
__END__
################################################
You may be interested in scsh: the scheme-shell.
That may be more interesting to you, because
scheme is closer to haskell then perl, I think.
Ciao,
Oliver
>> I've tried doing simple things in perl but its a worry.
>> Unless someones got suggestions about functional stylings in perl?
Yes, I have a suggestions - don't try it.
Use scsh (the scheme shell) instead.
--
"There is no right place for the dead to live."
-- Kai, last of the Brunnen-G
http://www.ps.uni-sb.de/~duchier/mogul/doc/mathweb/mosh/mathweb-mosh.html
-- Torbjörn
----------------------------------------------------------------
Torbjörn Lager Computational Linguist
Department of Linguistics Uppsala University
S-751 20 Uppsala Sweden
Phone: +46 18 471 7860 http://stp.ling.uu.se/~torbjorn/
----------------------------------------------------------------
The interact function gives me a warm fuzzy feeling
since it allows one to write unix pipeline/filter things
really easily.
However, whats missing is the ability to run a program
with system and to take its std out into my haskell
program...
If I could achieve this I would be pretty well free of perl...
:)
Seconded! and they even have their own newsgroup: news:comp.lang.scheme.scsh
--
Biep
Reply via http://www.biep.org
> However, whats missing is the ability to run a program
> with system and to take its std out into my haskell
> program...
>
> If I could achieve this I would be pretty well free of perl...
> :)
Well, attached is a first cut at what you want. It requires the Posix package
(so I suppose that leaves Hugs out). I tried it on GHC 5.00.1. Improvements
are welcome--please post them!
Dean Herington
Sorry for jumping in (I can't get the original article). The es shell is
not written in a functional language, but it does provide features of
functional languages.
http://www.webcom.com/~haahr/es/es-usenix-winter93.html
As someone mentioned, the underlying OS is still the same. In fact the
authors of es complain that they had to weaken the shell language to
accommodate the OS. Don't let that stop you from trying es, though!
-- Derek
> Since the only language I ever enjoyed programming in is haskell,
> and the only programming I have to do at the moment is
> shell scripting, ...
Not quite the answer you were looking for... but the Glasgow
Haskell distribution *used to* (and may still) include a toy
shell written in Haskell (Hsh.hs); by either Jim Mattson or
Simon Marlow, I think. A copy of some random version below...
Will
========================
module Main (main) where
import IO
import Posix
import Directory (setCurrentDirectory)
import System ( getEnv, exitWith, ExitCode(..) )
import Char (isSpace)
main :: IO ()
main =
do
initialize
commandLoop
{-
Standard shell practice: move std descriptors out of the way so
it's more convenient to set them up for children. Also set up an
interrupt handler which will put us back in the main loop.
-}
initialize :: IO ()
initialize =
dupTo stdInput myStdin >>
dupTo stdOutput myStdout >>
dupTo stdError myStderr >>
fdClose stdInput >>
fdClose stdOutput >>
-- fdClose stdError >>
installHandler sigINT (Catch intr) Nothing >>
return ()
-- some random fd numbers...
myStdin = intToFd 16
myStdout = intToFd 17
myStderr = intToFd 18
-- For user interrupts
intr :: IO ()
intr =
fdWrite myStdout "\n" >>
commandLoop
{-
Simple command loop: print a prompt, read a command, process the command.
Repeat as necessary.
-}
commandLoop :: IO ()
commandLoop =
fdWrite myStdout "$ " >>
try (readCommand myStdin) >>=
either
(\ err ->
if isEOFError err then
return ()
else
dieHorribly)
(\ cmd ->
try (processCommand cmd) >>= either (\ err -> commandLoop) (\ succ -> commandLoop))
where
dieHorribly :: IO ()
dieHorribly =
do
errMsg "read failed"
exitWith (ExitFailure 1)
{-
Read a command a character at a time (to allow for fancy processing later).
On newline, you're done, unless the newline was escaped by a backslash.
-}
readCommand :: Fd -> IO String
readCommand fd =
accumString "" >>= \ cmd ->
return cmd
where
accumString :: String -> IO String
accumString s =
myGetChar fd >>= \ c ->
case c of
'\\' ->
myGetChar fd >>= \ c' ->
accumString (c':c:s)
'\n' -> return (reverse s)
ch -> accumString (ch:s)
myGetChar :: Fd -> IO Char
myGetChar chan =
do
(s,len) <- fdRead chan 1
case len of
0 -> myGetChar chan
1 -> return (head s)
{-
To process a command, first parse it into words, then do the necessary
redirections, and finally perform the desired command. Built-ins are
checked first, and if none match, we execute an external command.
-}
processCommand :: String -> IO ()
processCommand "" = return ()
processCommand s =
do
words <- parseCommand s
(inFile, outFile, words) <- parseRedirection words
performRedirections inFile outFile
let
cmd = head words
args = tail words
case builtin cmd of
Just f ->
do
f args
fdClose stdInput
fdClose stdOutput
Nothing -> exec cmd args
{-
Redirections are a bit of a pain, really. If none are specified, we
dup our own file descriptors. Otherwise, we try to open the files
as requested.
-}
performRedirections :: Maybe String -> Maybe String -> IO ()
performRedirections inFile outFile =
(case inFile of
Nothing -> dupTo myStdin stdInput
Just x ->
try (openFd x ReadOnly Nothing defaultFileFlags)
>>=
either
(\ err ->
errMsg ("Can't redirect input from " ++ x) >>
fail (userError "redirect"))
(\ succ -> return ())) >>
(case outFile of
Nothing ->
dupTo myStdout stdOutput
Just x ->
try (createFile x stdFileMode) >>=
either
(\ err ->
do
errMsg ("Can't redirect output to " ++ x)
fdClose stdInput
fail (userError "redirect"))
(\ succ -> return ()))
{-
We parse a command line into words according to the following rules:
1) Anything inside pairs of "" or '' is parsed literally.
2) Anything (outside of quotes) escaped by \ is taken literally.
3) '<' and '>' are words all by themselves, unless escaped or quoted.
4) Whitespace separates words
-}
parseCommand :: String -> IO [String]
parseCommand = getTokens []
where
getTokens :: [String] -> String -> IO [String]
getTokens ts "" = return (reverse ts)
getTokens ts (c:cs) | isSpace c = getTokens ts cs
getTokens ts s =
getToken s >>= \ (t, s') ->
getTokens (t:ts) s'
getToken :: String -> IO (String, String)
getToken (c:cs)
| c == '<' || c == '>' = return ([c], cs)
| c == '"' || c == '\'' = accumQuote c "" cs
| otherwise = accumToken [c] cs
accumToken :: [Char] -> String -> IO (String, String)
accumToken cs "" = return (reverse cs, "")
accumToken cs ('\\':c:s) = accumToken (c:cs) s
accumToken cs x@(c:s)
| isSpace c || c == '<' || c == '>' = return (reverse cs, x)
| c == '"' || c == '\'' = accumQuote c cs s
| otherwise = accumToken (c:cs) s
accumQuote :: Char -> [Char] -> String -> IO (String, String)
accumQuote q cs "" =
errMsg ("Unmatched " ++ [q]) >>
fail (userError "unmatched quote")
accumQuote q cs (c:s)
| c == q = accumToken cs s
| otherwise = accumQuote q (c:cs) s
{-
Here we look for "<" and ">". When we find one, we remove it and the
following word from the word list. The arguments following the redirection
symbols and the remaining words are returned to our caller. However, it's
an error to end a word list with a redirection or for the same redirection
to appear twice.
-}
parseRedirection :: [String] -> IO (Maybe String, Maybe String, [String])
parseRedirection = redirect Nothing Nothing []
where
redirect inFile outFile args [] =
return (inFile, outFile, reverse args)
redirect inFile outFile args [arg]
| arg == "<" || arg == ">" =
errMsg "Missing name for redirect" >>
fail (userError "parse redirect")
| otherwise =
return (inFile, outFile, reverse (arg:args))
redirect inFile outFile args ("<":name:more)
| inFile == Nothing =
redirect (Just name) outFile args more
| otherwise =
errMsg "Ambiguous input redirect" >>
fail (userError "parse redirect")
redirect inFile outFile args (">":name:more)
| outFile == Nothing =
redirect inFile (Just name) args more
| otherwise =
errMsg "Ambiguous output redirect" >>
fail (userError "parse redirect")
redirect inFile outFile args (arg:more) =
redirect inFile outFile (arg:args) more
{-
Executing an external command is pretty simple, but what if it fails?
Fortunately, we don't have any way to redirect stdError just yet,
so we let it complain and then exit.
-}
exec :: String -> [String] -> IO ()
exec cmd args =
forkProcess >>= \ maybe_pid ->
case maybe_pid of
Nothing ->
do
dupTo myStderr stdError
fdClose myStdin
fdClose myStdout
fdClose myStderr
executeFile cmd True args Nothing
`catch`
(\ err ->
fdWrite stdError ("command not found: " ++ cmd ++ ".\n") >>
exitImmediately (ExitFailure 1))
Just pid ->
do
fdClose stdInput
fdClose stdOutput
-- fdClose stdError
getProcessStatus True False pid
return ()
{-
Builtins:
cd [arg] -> change directory (default to HOME)
exit ... -> exit successfully
Builtins must provide their own error messages, since the main command
loop ignores any errors.
-}
builtin :: String -> Maybe ([String] -> IO ())
builtin "cd" = Just chdir
builtin "exit" = Just exit
builtin _ = Nothing
chdir :: [String] -> IO ()
chdir [] =
do
home <- getEnv "HOME"
setCurrentDirectory home `catch` \ err -> errMsg "cd: can't go home"
chdir [dir] =
do
setCurrentDirectory dir `catch` \ err -> errMsg ("cd: can't chdir to " ++ dir)
chdir _ = errMsg "cd: too many arguments"
exit :: [String] -> IO ()
exit _ = exitWith ExitSuccess
-- Print an error message to my std error.
errMsg :: String -> IO ()
errMsg msg =
fdWrite myStderr ("hsh: " ++ msg ++ ".\n") >>
return ()