'os.Getenv and os.LookupEnv don't return the value of $HISTFILE, $HISTSIZE, and $SAVEHIST

I'm using zsh, and I'm trying to access these environment variables:

$ echo $HISTFILE
/home/amir/.zsh_history
$ echo $HISTSIZE
50000
$ echo $SAVEHIST
10000

But os.LookupEnv returns "", false for all three, and os.Getenv, naturally, returns an empty string:

package main

import (
    "fmt"
    "os"
)

func main() {
    histfile, found := os.LookupEnv("HISTFILE")
    if found {
        fmt.Println(histfile)
    } else {
        fmt.Println("$HISTFILE not found")
    }

    histsize, found := os.LookupEnv("HISTSIZE")
    if found {
        fmt.Println(histsize)
    } else {
        fmt.Println("$HISTSIZE not found")
    }

    savehist, found := os.LookupEnv("SAVEHIST")
    if found {
        fmt.Println(savehist)
    } else {
        fmt.Println("$SAVEHIST not found")
    }
}
$ go run main.go
$HISTFILE not found
$HISTSIZE not found
$SAVEHIST not found

Now, if I export these variables in $HOME/.zshrc:

$ grep -iE "histfile|histsize|savehist" $HOME/.zshrc
export HISTFILE="$HOME/.zsh_history"
export HISTSIZE=1000000
export SAVEHIST=1000000

Then it works and correct values are returned:

$ go run hyst.go
/home/amir/.zsh_history
1000000
1000000

When I haven't explicitly exported these variables, the values of $HISTSIZE and $SAVEHIST are different, but they're not empty. So why do these functions return empty strings for these variables?



Solution 1:[1]

That's because $HISTFILE, $HISTSIZE, and $SAVEHIST are not, by default, environment variables, but rather simply shell variables set by oh-my-zsh:

## History file configuration
[ -z "$HISTFILE" ] && HISTFILE="$HOME/.zsh_history"
[ "$HISTSIZE" -lt 50000 ] && HISTSIZE=50000
[ "$SAVEHIST" -lt 10000 ] && SAVEHIST=10000

These are the values you see when you use echo before using export in $HOME/.zshrc.

It's important to make a distinction between environment and shell variables:

  • Environment variables are accessible in child processes, but shell variables are not. You can verify this statement by simply creating a child process:

    $ # Child processes don't inherit shell variables
    $ key=value; sh -c 'echo "key=$key"'
    key=
    
    $ # They do, however, inherit environment variables
    $ export key=value; sh -c 'echo "key=$key"'
    key=value
    
  • To be able to access a shell variable in the child process, you can export it, just as you did in $HOME/.zshrc:

    $ key=value; export key; sh -c 'echo "key=$key"'
    key=value
    

Now, to see if a specific variable is an environment variable or not, instead of using echo, you can directly check the list of environment variables.

$ env | grep -i "shell"; echo $?
SHELL=/usr/bin/zsh
0

So $SHELL is, in fact, an environment variable. Now for the history variables:

$ env | grep -iE "histfile|histsize|savehist"; echo $?
1

The exit code is 1, meaning that it failed to find these names in the list of environment variables.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Amir Shabani