-- #hide module HsShellScript.Shell where import Data.List -- import System -- | -- Generate command (for a shell) which corresponds to the specified program -- name and argument list. The program name and arguments are the usual -- parameters for calling an external program, like when using -- @runProcess@ or @run@. The generated shell command -- would achieve the same effect. The name and the arguments are properly -- quoted, using 'shell_quote'. -- -- Note: The quoted strings are correctly recognized in shell scripts. But the shell bash has an annoying history expansion \"feature\", which causes -- it to choke on exclamation marks, when in interactive mode, even when quoted with double quotes. You can turn it off with @set +o histexpand@. shell_command :: String -- ^ name or path of the executable -> [String] -- ^ command line arguments -> String -- ^ shell command shell_command k par = concat (intersperse " " (map shell_quote (k:par))) -- | -- Quote shell metacharacters. -- -- This function quotes strings, such that they are not misinterpreted by -- the shell. It tries to be friendly to a human reader - when special -- characters are present, then the string is quoted with double quotes. If -- not, it is left unchanged. -- -- The list of exacly which characters need to be quoted has been taken -- from the bash source code. Bash in turn, implements POSIX 1003.2. So the -- result produced should be correct. From the bash info pages: -- \"... the rules for evaluation and quoting are taken from the POSIX -- 1003.2 specification for the `standard' Unix shell.\" -- -- Note: The quoted strings are correctly recognized in shell scripts. But the shell bash has an annoying history expansion \"feature\", which causes -- it to choke on exclamation marks, when in interactive mode, even when quoted with double quotes. You can turn it off with @set +o histexpand@. -- -- See 'quote'. shell_quote :: String -> String shell_quote "" = "\"\"" shell_quote txt = let need_to_quote c = c `elem` "' \t\n\"\\|&;()<>!{}*[?]^$`#" in if any need_to_quote txt then '"' : quote0' txt else txt where quote0' :: String -> String quote0' (z:zs) = if (z `elem` "\"$`\\") then ('\\':(z:(quote0' zs))) else (z:(quote0' zs)) quote0' "" = "\"" -- | -- Quote special characters inside a string for the shell -- -- This quotes special characters inside a string, such that it is -- recognized as one string by the shell when enclosed in double quotes. -- Doesn't add the double quotes. -- -- Note: The quoted strings are correctly recognized in shell scripts. But the shell bash has an annoying history expansion \"feature\", which causes -- it to choke on exclamation marks, when in interactive mode, even when quoted with double quotes. You can turn it off with @set +o histexpand@. -- -- See 'quote', 'shell_quote'. quote0 :: String -> String quote0 (z:zs) = if (z `elem` "\"$`\\") then ('\\':(z:(quote0 zs))) else (z:(quote0 zs)) quote0 "" = "" -- | -- Quote a string for the shell -- -- This encloses a string in double quotes and quotes any special -- characters inside, such that it will be recognized as one string by a -- shell. The double quotes are added even when they aren't needed for this -- purpose. -- -- Note: The quoted strings are correctly recognized in shell scripts. But the shell bash has an annoying history expansion \"feature\", which causes -- it to choke on exclamation marks, when in interactive mode, even when quoted with double quotes. You can turn it off with @set +o histexpand@. -- -- See 'quote0', 'shell_quote'. quote :: String -> String quote str = "\"" ++ quote0 str ++ "\""