Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I set an environment variable in a cross-platform way?

I can easily read an environment variable using System.Environment.getEnv. However, System.Environment does not have any corresponding setEnv function (as far as I could tell).

How do I set an environment variable from a Haskell program? I would prefer a cross-platform solution. (So just executing export VAR=val or using System.Posix.Env is not quite what I'm looking for.)

like image 641
Tikhon Jelvis Avatar asked Feb 14 '12 10:02

Tikhon Jelvis


People also ask

What is cross env command?

cross-env makes it so you can have a single command without worrying about setting or using the environment variable properly for the platform. Just set it like you would if it's running on a POSIX system, and cross-env will take care of setting it properly. Installation. Usage.

How do I set an environment variable in export?

To set an environment variable, use the command " export varname=value ", which sets the variable and exports it to the global environment (available to other processes). Enclosed the value with double quotes if it contains spaces. To set a local variable, use the command " varname =value " (or " set varname =value ").


1 Answers

Note that base 4.7.0 now has a setEnv in System.Environment. So for the present or near future it's sorted out.

However if you need this feature in versions <4.7.0 (which is my case currently), I have also extracted from the commit that adds the feature the functions needed to make this work with an older base version.

However I seriously lost patience on this one and did it rather ugly but it works for me...

The mess is that there are 3 functions to call in a windows environment: putenv, SetEnvironmentVariableA (ASCII) and SetEnvironmentVariableW (widechar, utf16). The patch that was committed to base 4.7 does this automatically, but I did something more ugly by lack of time (I may yet it clean it up).

Here's what I have:

setEnv_ :: String -> String -> IO () 
setEnv_ key value = withCString key $ \k -> withCString value $ \v -> do 
  success <- c_SetEnvironmentVariable k v 
  unless success (throwGetLastError "setEnv") 

putEnv :: String -> IO ()
putEnv v = void (withCString v $ \vv -> c_putenv vv)

foreign import stdcall unsafe "windows.h SetEnvironmentVariableA" 
  c_SetEnvironmentVariable :: CString -> CString -> IO Bool 

-- SetEnv_ :: String -> String -> IO () 
-- SetEnv_ key value = withCWString key $ \k -> withCWString value $ \v -> do 
--   success <- c_SetEnvironmentVariable k v 
--   unless success (throwGetLastError "setEnv") 
--  
-- Foreign import stdcall unsafe "windows.h SetEnvironmentVariableW" 
--   c_SetEnvironmentVariable :: LPTSTR -> LPTSTR -> IO Bool 

foreign import ccall unsafe "putenv" c_putenv :: CString -> IO CInt 

Obviously use CPP to put the whole thing in a #ifdef for windows only. As you can see I have the code for the widechar call, but I commented it currently. I think for my use-case it would probably be enough to just call putenv but well it works as it is. So here's how I call it then:

setEnv_ "LANG" localeStr
putEnv $ "LANG=" ++ localeStr

My problem is that I'm primarily a linux user at home and I don't like doing too much work on windows at home, and I put a lot of energy to get this and other things to work properly on windows, and I can't bring myself to clean this up further. But with this code and the original patch you should get this to work on base <4.7 without much problems.

like image 132
Emmanuel Touzery Avatar answered Oct 03 '22 03:10

Emmanuel Touzery