@@ -16,13 +16,16 @@ package godotenv
16
16
import (
17
17
"bufio"
18
18
"errors"
19
+ "fmt"
19
20
"io"
20
21
"os"
21
22
"os/exec"
22
23
"regexp"
23
24
"strings"
24
25
)
25
26
27
+ const doubleQuoteSpecialChars = "\\ \n \r \" !$`"
28
+
26
29
// Load will read your env file(s) and load them into ENV for this process.
27
30
//
28
31
// Call this function as close as possible to the start of your program (ideally in main)
@@ -119,6 +122,11 @@ func Parse(r io.Reader) (envMap map[string]string, err error) {
119
122
return
120
123
}
121
124
125
+ //Unmarshal reads an env file from a string, returning a map of keys and values.
126
+ func Unmarshal (str string ) (envMap map [string ]string , err error ) {
127
+ return Parse (strings .NewReader (str ))
128
+ }
129
+
122
130
// Exec loads env vars from the specified filenames (empty map falls back to default)
123
131
// then executes the cmd specified.
124
132
//
@@ -136,6 +144,30 @@ func Exec(filenames []string, cmd string, cmdArgs []string) error {
136
144
return command .Run ()
137
145
}
138
146
147
+ // Write serializes the given environment and writes it to a file
148
+ func Write (envMap map [string ]string , filename string ) error {
149
+ content , error := Marshal (envMap )
150
+ if error != nil {
151
+ return error
152
+ }
153
+ file , error := os .Create (filename )
154
+ if error != nil {
155
+ return error
156
+ }
157
+ _ , err := file .WriteString (content )
158
+ return err
159
+ }
160
+
161
+ // Marshal outputs the given environment as a dotenv-formatted environment file.
162
+ // Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped.
163
+ func Marshal (envMap map [string ]string ) (string , error ) {
164
+ lines := make ([]string , 0 , len (envMap ))
165
+ for k , v := range envMap {
166
+ lines = append (lines , fmt .Sprintf (`%s="%s"` , k , doubleQuoteEscape (v )))
167
+ }
168
+ return strings .Join (lines , "\n " ), nil
169
+ }
170
+
139
171
func filenamesOrDefault (filenames []string ) []string {
140
172
if len (filenames ) == 0 {
141
173
return []string {".env" }
@@ -264,3 +296,17 @@ func isIgnoredLine(line string) bool {
264
296
trimmedLine := strings .Trim (line , " \n \t " )
265
297
return len (trimmedLine ) == 0 || strings .HasPrefix (trimmedLine , "#" )
266
298
}
299
+
300
+ func doubleQuoteEscape (line string ) string {
301
+ for _ , c := range doubleQuoteSpecialChars {
302
+ toReplace := "\\ " + string (c )
303
+ if c == '\n' {
304
+ toReplace = `\n`
305
+ }
306
+ if c == '\r' {
307
+ toReplace = `\r`
308
+ }
309
+ line = strings .Replace (line , string (c ), toReplace , - 1 )
310
+ }
311
+ return line
312
+ }
0 commit comments