simple-cmd 0.1.4 → 0.2.0
raw patch · 4 files changed
+148/−26 lines, 4 filesdep ~processPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: process
API changes (from Hackage documentation)
+ SimpleCmd: pipe :: PipeCommand -> PipeCommand -> IO String
+ SimpleCmd: pipe3_ :: PipeCommand -> PipeCommand -> PipeCommand -> IO ()
+ SimpleCmd: pipeBool :: PipeCommand -> PipeCommand -> IO Bool
+ SimpleCmd: pipeFile_ :: FilePath -> PipeCommand -> PipeCommand -> IO ()
+ SimpleCmd: pipe_ :: PipeCommand -> PipeCommand -> IO ()
+ SimpleCmd: shellBool :: String -> IO Bool
+ SimpleCmd: sudo_ :: String -> [String] -> IO ()
+ SimpleCmd: type PipeCommand = (String, [String])
+ SimpleCmd: warning :: String -> IO ()
- SimpleCmd: sudo :: String -> [String] -> IO ()
+ SimpleCmd: sudo :: String -> [String] -> IO String
Files
- ChangeLog.md +9/−2
- README.md +12/−2
- SimpleCmd.hs +122/−17
- simple-cmd.cabal +5/−5
ChangeLog.md view
@@ -1,10 +1,17 @@ # Revision history for simple-cmd -## 0.1.4+## 0.2.0 (2019-06-03)+- add warning command+- API change: sudo and sudo_+- add shellBool+- add pipe, pipe_, pipeBool, pipeFile_, pipe3+- quoteCmd uses showCommandForUser++## 0.1.4 (2019-04-08) - export error' - add cmdLog (deprecates cmdlog) -## 0.1.3.1+## 0.1.3.1 (2019-03-15) - sudo: ignored for root or when no sudo installed ## 0.1.3 -- 2019-02-20
README.md view
@@ -18,6 +18,7 @@ outputs to stdout. For example ```haskell+cmd_ "echo" ["Hello"] cmd_ "git" ["clone", url] ``` This can shortened to `git_ "clone" [url]`.@@ -25,15 +26,24 @@ ```haskell cmd :: String -> [String] -> IO String ```-returns stdout as a `String`.+returns stdout as a `String`. eg +```haskell+date <- cmd "date" []+```+ There are also `cmdBool`, `cmdMaybe`, `cmdList`, `shell`, and others. +Simple pipes are also supported:+```haskell+pipe_ ("echo",["hello"]) ("grep",["ello"])+```+ Other examples: ```haskell gitBranch :: IO String grep_ pat file :: IO Bool-sudo c args :: IO ()+sudo_ c args :: IO () ``` See the library documentation for more details.
SimpleCmd.hs view
@@ -32,7 +32,7 @@ cmdIgnoreErr, cmdLines, cmdMaybe,- cmdLog, cmdlog {-TODO: remove for 0.2 -},+ cmdLog, cmdlog {-TODO: remove for 0.3 -}, cmdN, cmdQuiet, cmdSilent,@@ -43,23 +43,31 @@ logMsg, removePrefix, removeStrictPrefix, removeSuffix, shell, shell_,- sudo,+ shellBool,+ sudo, sudo_,+ warning,+ PipeCommand,+ pipe, pipe_, pipeBool,+ pipe3_, pipeFile_, (+-+)) where #if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0)) #else import Control.Applicative ((<$>)) #endif-import Control.Monad (when)+import Control.Monad import Data.List (stripPrefix) import Data.Maybe (isNothing, fromMaybe) import System.Directory (findExecutable) import System.Exit (ExitCode (..))-import System.IO (hPutStrLn, stderr)+import System.IO (hGetContents, hPutStrLn, IOMode(ReadMode), stderr, withFile) import System.Posix.User (getEffectiveUserID)-import System.Process (readProcess, readProcessWithExitCode, rawSystem)+import System.Process (createProcess, proc, ProcessHandle, rawSystem, readProcess,+ readProcessWithExitCode, runProcess, showCommandForUser,+ std_in, std_out, StdStream(CreatePipe, UseHandle),+ waitForProcess, withCreateProcess) removeTrailingNewline :: String -> String removeTrailingNewline "" = ""@@ -69,7 +77,7 @@ else str quoteCmd :: String -> [String] -> String-quoteCmd c args = "'" ++ unwords (c:args) ++ "'"+quoteCmd = showCommandForUser -- | Alias for errorWithoutStackTrace (for base >= 4.9) --@@ -95,14 +103,18 @@ ExitSuccess -> return () ExitFailure n -> error' $ quoteCmd c args +-+ "failed with exit code" +-+ show n --- | 'cmdBool c args' runs a command, and return Boolean status-cmdBool :: String -> [String] -> IO Bool-cmdBool c args = do- ret <- rawSystem c args+boolWrapper :: IO ExitCode -> IO Bool+boolWrapper pr = do+ ret <- pr case ret of ExitSuccess -> return True ExitFailure _ -> return False +-- | 'cmdBool c args' runs a command, and return Boolean status+cmdBool :: String -> [String] -> IO Bool+cmdBool c args =+ boolWrapper (rawSystem c args)+ -- | 'cmdMaybe c args' runs a command, maybe returning output if it succeeds cmdMaybe :: String -> [String] -> IO (Maybe String) cmdMaybe c args = do@@ -125,10 +137,17 @@ shell :: String -> IO String shell cs = cmd "sh" ["-c", cs] --- | 'shell_ c' runs a command string in a shell, output goes to stdout+-- | 'shell_ cs' runs a command string in a shell, output goes to stdout shell_ :: String -> IO ()-shell_ c = cmd_ "sh" ["-c", c]+shell_ cs = cmd_ "sh" ["-c", cs] +-- | 'shellBool cs' runs a command string in a shell, output goes to stdout+--+-- @since 0.2.0+shellBool :: String -> IO Bool+shellBool cs =+ boolWrapper (rawSystem "sh" ["-c", cs])+ -- | 'cmdLog c args' logs a command with a datestamp -- -- @since 0.1.4@@ -137,7 +156,7 @@ logMsg $ unwords $ c:args cmd_ c args --- | 'cmdlog' deprecated alias for 'cmdLog' (to be removed in 0.2)+-- | 'cmdlog' deprecated alias for 'cmdLog' (will be removed in 0.3) cmdlog :: String -> [String] -> IO () cmdlog = cmdLog @@ -206,19 +225,32 @@ egrep_ pat file = cmdBool "grep" ["-q", "-e", pat, file] --- | 'sudo c args' runs a command as sudo+-- | 'sudo c args' runs a command as sudo returning stdout+--+-- Result type changed from IO () to IO String in 0.2.0 sudo :: String -- ^ command -> [String] -- ^ arguments+ -> IO String+sudo = sudoInternal cmd++-- | 'sudo_ c args' runs a command as sudo+--+-- @since 0.2.0+sudo_ :: String -- ^ command+ -> [String] -- ^ arguments -> IO ()-sudo c args = do+sudo_ = sudoInternal cmdLog++sudoInternal :: (String -> [String] -> IO a) -> String -> [String] -> IO a+sudoInternal exc c args = do uid <- getEffectiveUserID sd <- if uid == 0 then return Nothing else findExecutable "sudo" let noSudo = isNothing sd when (uid /= 0 && noSudo) $- hPutStrLn stderr "'sudo' not found"- cmdLog (fromMaybe c sd) (if noSudo then args else c:args)+ warning "'sudo' not found"+ exc (fromMaybe c sd) (if noSudo then args else c:args) -- | Combine two strings with a single space infixr 4 +-+@@ -249,3 +281,76 @@ fromMaybe orig $ stripSuffix suffix orig where stripSuffix sf str = reverse <$> stripPrefix (reverse sf) (reverse str)++-- | 'warning' outputs to stderr+--+-- @since 0.2.0+warning :: String -> IO ()+warning = hPutStrLn stderr++type PipeCommand = (String,[String])++-- | Return stdout from piping the output of one process to another+--+-- @since 0.2.0+pipe :: PipeCommand -> PipeCommand -> IO String+pipe (c1,args1) (c2,args2) =+ withCreateProcess ((proc c1 args1) { std_out = CreatePipe }) $+ \ _si (Just ho1) _se p1 -> do+ (_, Just ho2, _, p2) <- createProcess ((proc c2 args2) {std_in = UseHandle ho1, std_out = CreatePipe})+ out <- hGetContents ho2+ void $ waitForProcess p1+ void $ waitForProcess p2+ return out++-- | Pipe two commands without returning anything+--+-- @since 0.2.0+pipe_ :: PipeCommand -> PipeCommand -> IO ()+pipe_ (c1,args1) (c2,args2) =+ void $ pipeInternal (c1,args1) (c2,args2) >>= waitForProcess++-- | Bool result of piping of commands+--+-- @since 0.2.0+pipeBool :: PipeCommand -> PipeCommand -> IO Bool+pipeBool (c1,args1) (c2,args2) =+ boolWrapper $ pipeInternal (c1,args1) (c2,args2) >>= waitForProcess++pipeInternal :: PipeCommand -> PipeCommand -> IO ProcessHandle+pipeInternal (c1,args1) (c2,args2) =+ -- nicer with process-typed:+ -- withProcess_ (setStdout createPipe proc1) $ \ p -> runProcess (setStdin (useHandleClose (getStdout p)) proc2)+ withCreateProcess ((proc c1 args1) { std_out = CreatePipe }) $+ \ _si so _se p1 -> do+ p2 <- runProcess c2 args2 Nothing Nothing so Nothing Nothing+ void $ waitForProcess p1+ return p2++-- | Pipe 3 commands, no returning anything+--+-- @since 0.2.0+pipe3_ :: PipeCommand -> PipeCommand -> PipeCommand -> IO ()+pipe3_ (c1,a1) (c2,a2) (c3,a3) =+ withCreateProcess ((proc c1 a1) { std_out = CreatePipe }) $+ \ _hi1 (Just ho1) _he1 p1 ->+ withCreateProcess ((proc c2 a2) {std_in = UseHandle ho1, std_out = CreatePipe}) $+ \ _hi2 ho2 _he2 p2 -> do+ p3 <- runProcess c3 a3 Nothing Nothing ho2 Nothing Nothing+ void $ waitForProcess p1+ void $ waitForProcess p2+ void $ waitForProcess p3++-- | Pipe a file to the first of a pipe of commands+--+-- @since 0.2.0+pipeFile_ :: FilePath -> PipeCommand -> PipeCommand -> IO ()+pipeFile_ infile (c1,a1) (c2,a2) =+ withFile infile ReadMode $+ \ hin ->+ withCreateProcess ((proc c1 a1) { std_in = UseHandle hin, std_out = CreatePipe }) $+ \ _si so _se p1 -> do+ p2 <- runProcess c2 a2 Nothing Nothing so Nothing Nothing+ void $ waitForProcess p1+ void $ waitForProcess p2+
simple-cmd.cabal view
@@ -1,10 +1,10 @@ name: simple-cmd-version: 0.1.4+version: 0.2.0 synopsis: Simple String-based process commands description:- Thin wrappers over System.Process (readProcess,- readProcessWithExitCode, and rawSystem). The idea is to provide- some simple common idioms for easy calling out to commands+ Simple wrappers over System.Process+ (readProcess, readProcessWithExitCode, rawSystem, and createProcess). + The idea is to provide some common idioms for calling out to commands from programs. For more advanced shell-scripting or streaming use turtle, shelly, command, etc. license: BSD3@@ -18,7 +18,7 @@ extra-source-files: README.md ChangeLog.md TODO tested-with: GHC == 7.0.4, GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4,- GHC == 8.6.4+ GHC == 8.6.5 source-repository head type: git