본문 바로가기
Computer Science/Linux

[Linux] 쉘 변수와 환경변수

by yhames 2024. 2. 27.

 

이 글은 shell에서 환경 변수를 참조하는 방법과 env에서 쉘 변수를 어떻게 지정하는지에 대해 다루고 있습니다.

 

env 명령어

minishell 과제에서 built-in 명령어들은 모두 환경변수와 관련된 명령어 입니다.

env 명령어는 옵션을 제외하고 다음과 같이 3가지 방식으로 사용할 수 있습니다.

 

1. 환경변수 목록 출력

# 입력
env
# 출력
TERM_SESSION_ID=w0t0p0:4A2AC993-C7C8-4745-9387-F181921C1CC8
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.bGePyIUzEC/Listeners
LC_TERMINAL_VERSION=3.4.23
# ...

 

 

2. 하나 이상의 쉘 변수를 포함한 환경변수 목록을 출력

# 입력
env TEST=test
# 입력
TERM_SESSION_ID=w0t0p0:4A2AC993-C7C8-4745-9387-F181921C1CC8
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.bGePyIUzEC/Listeners
# ...
TEST=test	# 쉘 변수

 

 

3. 하나 이상의 쉘 변수를 환경 변수로 지정하여 특정 명령어 실행

# 입력
env {key=value} other_command	# other_command 명령어 실행

 

 

즉, env 명령어를 사용해서 출력되는 변수들의 목록은 환경변수 뿐만 아니라 env 명령어의 매개변수로 전달한 쉘 변수를 포함합니다.

 

하지만 해당 쉘 변수로 환경변수로 지정해서 특정 명령어로 실행을 하면 

해당 쉘 변수는 해당 명령어에서는 환경변수로 사용되지만 현재 쉘 에서는 환경변수로 저장되지 않습니다.

 

쉘 변수

쉘 변수란 해당 쉘 스크립트 혹은 쉘 세션에서 임시로 사용하는 변수를 의미합니다.

쉘 스크립트에서 정의되는 변수로서 쉘 세션에서도 사용될 수 있습니다.

 

쉘 변수를 할당하는 방법은 다음과 같습니다.

쉘 변수는 문자열이나 숫자 혹은 배열도 가능합니다.

# 문자열 할당
name="John"

# 숫자 할당
number=10

# 배열 할당
my_array=(apple banana cherry)

 

환경 변수

환경변수란 프로세스가 컴퓨터에서 동작하는 방식에 영향을 미치는, 동적인 값들의 모임입니다.

쉽게 말해서 PATH, HOME 등 프로세스가 동작하는데 사용되는 운영체제의 전역 변수라고 생각하면 됩니다.

쉘에서 환경변수를 조작하는 방법은 다음과 같이 3가지가 있습니다.

  • envp 매개변수
  • environ 전역변수
  • getenv() 함수

환경 변수를 담은 문자열 배열

이들이 가져오는 환경 변수가 정확히 어디에 저장되어있는지 확인하기 위해

다음과 같이 각각의 방식으로 환경변수를 주소와 함께 출력하는 테스트를 작성했습니다.

더보기
// env_test.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

int main(int argc, char **argv, char **envp)
{
    int        i;
    char    *str;

    printf("+===========================+\n");
    printf("|          environ          |\n");
    printf("+===========================+\n");
    i = 0;
    while (environ[i])
    {
        if (strncmp(envp[i], "TEST", 4) == 0)
            printf("[%p] %s\n", environ[i], environ[i]);
        i++;
    }
    printf("\n");

    printf("+===========================+\n");
    printf("|           envp            |\n");
    printf("+===========================+\n");
    i = 0;
    while (envp[i])
    {
        if (strncmp(envp[i], "TEST", 4) == 0)
            printf("[%p] %s\n", envp[i], envp[i]);
        i++;
    }
    printf("\n");

    printf("+===========================+\n");
    printf("|           getenv          |\n");
    printf("+===========================+\n");
    str = getenv("TEST");
    printf("[%p] %s\n", str, str);
    if (str)
        printf("[%p] %s\n", str - 5, str - 5);
    return (0);
}

 

테스트 코드를 실행하기 전에 다음과 같이 환경 변수를 설정해야합니다.

export TEST=test

 

테스트 결과는 다음과 같습니다.

+===========================+
|          environ          |
+===========================+
[0x16bb4fe56] TEST=test

+===========================+
|           envp            |
+===========================+
[0x16bb4fe56] TEST=test

+===========================+
|           getenv          |
+===========================+
[0x16bb4fe5b] test
[0x16bb4fe56] TEST=test

 

테스트 결과 모두 같은 주소를 가리키고 있습니다.

즉, envrionenvp는 모두 같은 문자열 배열을 가리키고 있고

getenv() 함수를 사용할 경우 해당 문자열 배열의 value 부분("key=value" 형식의 문자열에서 = 뒷부분)을 가리키고 있음을 알 수 있습니다.

 

envp 매개변수에 문자열 배열이 할당되는 시점

그렇다면 envp 변수에 환경변수 문자열 배열이 언제 할당이 되는 것일까요?

이를 확인하기 위해 execve() 함수를 사용하여 위의 테스트 코드를 실행하는 exec_test_01 테스트를 작성했습니다.

더보기
// exec_test_01.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

char *strjoin(char *s1, char *s2)
{
    char    *str;
    size_t    s1_len;
    size_t    s2_len;

    s1_len = strlen(s1);
    s2_len = strlen(s2);
    str = (char *)malloc(sizeof(char) * (s1_len + s2_len + 1));
    if (!str)
        return (NULL);
    memcpy(str, s1, s1_len);
    memcpy(str + s1_len, s2, s2_len);
    str[s1_len + s2_len] = '\0';
    return (str);
}

int main(int argc, char **argv, char **envp)
{
    char    *pwd;
    char    *cmd;

    pwd = getcwd(NULL, 0);
    cmd = strjoin(pwd, "/env_test.out");
    execve(cmd, NULL, NULL);
    free(pwd);
    return (0);
}

 

만약 execve()에서 3번째 매개변수(envp)에 NULL을 전달하면

쉘에서 환경변수가 설정되어 있더라도 다음과 같이 전달되지 않는 모습을 볼 수 있습니다.

+===========================+
|          environ          |
+===========================+

+===========================+
|           envp            |
+===========================+

+===========================+
|           getenv          |
+===========================+
[0x0] (null)

 

다른 테스트 케이스를 확인하기 위해 직접 문자열 배열을 만들어서 전달하는 exec_test_02를 작성했습니다.

더보기
// exec_test_02.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

char *strjoin(char *s1, char *s2)
{
    char    *str;
    size_t    s1_len;
    size_t    s2_len;

    s1_len = strlen(s1);
    s2_len = strlen(s2);
    str = (char *)malloc(sizeof(char) * (s1_len + s2_len + 1));
    if (!str)
        return (NULL);
    memcpy(str, s1, s1_len);
    memcpy(str + s1_len, s2, s2_len);
    str[s1_len + s2_len] = '\0';
    return (str);
}

int main(int argc, char **argv, char **envp)
{
    char    *pwd;
    char    *cmd;

    pwd = getcwd(NULL, 0);
    cmd = strjoin(pwd, "/env_test.out");
    execve(cmd, NULL, NULL);
    free(pwd);
    return (0);
}

 

태스트 결과 다음과 같이 출력됩니다.

+===========================+
|          environ          |
+===========================+
[0x16f843e50] TEST=test

+===========================+
|           envp            |
+===========================+
[0x16f843e50] TEST=test

+===========================+
|           getenv          |
+===========================+
[0x16f843e55] test
[0x16f843e50] TEST=test

 

즉, 환경변수 문자열 배열은 execve()와 마찬가지로 main()이 실행되기 전에 environ 변수에 저장이 되고,

해당 값이 envp 매개변수로 전달된 것이라고 예상할 수 있습니다.

 

env 명령어로 쉘 변수를 환경변수로 지정하기

env 명령어를 사용하면 특정 쉘 변수를 환경변수로 지정해서 다른 명령어를 실행할 수 있습니다.

이 때 환경변수로 지정된 쉘 변수는 다른 쉘 세션이나 자식 프로세스에 영향을 끼치지 않습니다.

 

이를 알아보기 위해 첫 번째 테스트(env_test)를 실행할 때 다음과 같은 명령어를 사용해서 테스트를 했습니다.

unset TEST; env TEST=test ./env_test.out

 

위 명령어를 실행하면 다음과 같은 결과가 나옵니다.

+===========================+
|          environ          |
+===========================+
[0x16d2ffe55] TEST=test

+===========================+
|           envp            |
+===========================+
[0x16d2ffe55] TEST=test

+===========================+
|           getenv          |
+===========================+
[0x16d2ffe5a] test
[0x16d2ffe55] TEST=test

 

하지만 다음 명령어를 사용해서 확인해보면 환경변수나 쉘 변수로 등록되어 있지 않다는 것을 알 수 있습니다.

export | grep TEST	# 환경변수로 등록되지 않고
echo $TEST		# 해당 세션의 쉘 변수로도 등록되지 않는다.

'Computer Science > Linux' 카테고리의 다른 글

[Linux] Flex와 Bison  (0) 2024.02.28
[Linux] SSH가 무엇인가요?  (0) 2023.11.28
[Linux] 접근 통제 방법 DAC와 MAC  (0) 2023.11.28
[Linux] Debian과 Rocky Linux  (0) 2023.11.28