컴퓨터/운영체제(주로 리눅스)

[리눅스] 환경 변수 개념과 환경 변수를 다루는 방법 - 환경 변수는 어쩌면 맛있는게 아닐까?

REAKWON 2023. 8. 3. 12:23

리눅스 시스템의 더 많은 정보와 예제를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.

https://reakwon.tistory.com/233

 

리눅스 프로그래밍 note 배포

티스토리에 리눅스에 관한 내용을 두서없이 여지껏 포스팅했었데요. 저도 제 포스팅을 찾기가 어렵기도 하고 티스토리에서 코드삽입을 하게 되면 이게 일자로 쭉 쓰여져있는 x같은 현상이 생겨

reakwon.tistory.com

 

환경 변수

컴퓨터 시스템에서 사용되는 동적인 값을 저장하는 변수, 프로그램들이 시스템 설정과 상호작용하는 데 사용됩니다. 환경변수는 텍스트 형식으로 저장되며, 특정 이름과 그에 해당하는 값으로 구성됩니다.

이름 =  

시스템 전체에서 공유되거나 특정 프로세스 내에서만 유효한 변수들이 있을 수 있습니다. 환경변수는 주로 다음과 같은 용도로 사용됩니다.

  1. 시스템 설정 및 구성 정보 저장 시스템의 설정 정보를 저장하여 다른 프로그램들이 해당 정보를 활용할 수 있게 합니다. 예를 들어, 시스템의 언어, 시간대, 기본 디렉토리 등을 환경변수로 설정하여 시스템 전반에 적용할 수 있습니다. 
  2. 프로그램 실행 시 동작 옵션 설정 프로그램이 동작할 때 필요한 옵션을 환경변수로 설정하여 해당 프로그램이 이를 인식하고 적용하게 할 수 있습니다. 이를 통해 특정 프로그램의 동작을 조정하거나 사용자 맞춤 설정을 지원할 수 있습니다.
  3. 실행 환경 설정 프로그램이 실행되는 환경을 설정하는 데 사용됩니다. 예를 들어, 실행 파일들을 찾는 경로를 설정하거나 라이브러리의 위치를 지정하는데 환경변수를 활용할 수 있습니다.

 우리는 현재 쉘에서 환경 변수를 설정한적은 없죠. 그런데 로그인 할 때 기본적으로 설정되어 있는 환경 변수들이 있습니다(여기서는 bash 쉘만 보겠습니다.) printenv 명령(혹은 env)을 통해서 현재 쉘에 어떤 환경변수가 설정되어있는지 확인해볼까요?

# printenv
SHELL=/bin/bash
PWD=/root
LOGNAME=ubuntu
//...
HOME=/root
LANG=en_US.UTF-8
//...
USER=ubuntu
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
MAIL=/var/mail/root
SSH_TTY=/dev/pts/0

제가 있는 현재 bash 쉘의 환경변수는 이렇게 설정되어있습니다. 환경 변수도 변수이기 때문에 그 값을 변경하거나 없애거나 할 수 있습니다. 그런데 보통 기본적으로 설정되어있는 환경 변수는 건드리지 않는 편이 좋습니다. 예를 들어 PATH를 건드리게 되면 명령어 실행을 못할 수도 있습니다. 여기서는 알아두면 좋을 환경 변수들을 설명하고 환경 변수를 설정하는 방법과 프로그램에서 어떻게 환경 변수를 읽어오고 설정하는지 확인해보도록 하겠습니다.

1. 일반적인 환경 변수들

아래의 환경 변수들은 기본적으로 현재 쉘에 설정되어있을 수 있는 환경 변수들입니다.

환경변수  설명
USER 현재 로그인한 사용자입니다. UID(숫자)가 아닌 계정명으로 나타납니다.
HOME 현재 사용자의 홈 디렉토리입니다. 로그인하면 이 디렉토리로 자동으로 위치가 됩니다.
EDITER 사용할 기본 파일 편집기입니다. 터미널에 edit를 입력할 때 사용할 편집기입니다.
LANG 현재 로컬 언어 설정을 보여줍니다.
TERM 현재 터미널 에뮬레이션입니다.
MAIL 현재 사용자의 메일이 저장되는 위치입니다.
PATH  명령어를 실행할 때 검색할 디렉토리들입니다. 여러 디렉토리가 있을 수 있기 때문에 :(콜론)으로 구분이 됩니다.
여러분이 명령어를 아무데서나 칠 수 있는 이유는 바로 이 PATH변수 때문입니다. 이 변수가 없다면 ls 명령을 칠 때 전체 경로를 넣어서 /bin/ls를 쳐야하지만 PATH/bin 디렉토리가 있기 때문에 ls만 쳐서 명령을 칠 수 있는 것입니다.
SHELL 현재 사용자의 로그인 쉘입니다.
PWD 현재 사용자가 있는 디렉토리입니다

 

2.  환경 변수 설정

우선 일반적으로 쉘에서 단순 변수는 아래와 같이 설정할 수 있습니다.

변수명= 

그 변수를 참조할 때는 변수명 앞에 $를 사용하여 확인할 수 있습니다. 며칠전에 달라로 500만원을 환전했는데 개떡락을 하더군요 --;;

# VAL="hello, world"
# echo $VAL
hello, world

그런데 이건 이 쉘에서만 유효한 변수일 뿐이지 환경 변수는 아닙니다. printenvenv 명령을 통해서 현재 쉘에 설정된 환경 변수확인해보세요. 환경변수는 아니라는 것을 알 수 있습니다.

# printenv | grep VAL
아무 결과도 나오지 않음

대신에 현재 쉘 환경의 환경 변수를 포함한 모든 변수, 함수 등을 출력하는 set 명령을 통해 확인하면 변수가 설정되어있는 것은 알 수 있습니다.

# set | grep VAL
VAL='hello world'

그렇다면 환경 변수를 설정하려면 어떻게 해야 할까요?

 export 명령

export 명령, 일명 내보내기 명령을 통해서 현재 쉘의 환경 변수로 변수를 등록할 수 있습니다. 이때 변수명 앞 $는 생략해야합니다.

export 변수명

# VAL="hello world"
# export VAL
# printenv | grep VAL
VAL=hello world

혹은 한번에 값을 지정해서 내보낼 수도 있습니다.

export 변수명=

# export APPLE="Mac Book"
# printenv | grep APPLE
APPLE=Mac Book

이제 환경 변수로 등록되었다는 것을 알 수 있습니다.  

환경 변수나 변수를 해제하려면 unset 명령어를 통해서 해제할 수 있습니다.

unset 변수명

# printenv |grep VAL
VAL=hello world
# unset VAL
# printenv | grep VAL
# printenv | grep APPLE
APPLE=Mac Book
# unset APPLE
# printenv | grep APPLE

이런 환경 변수는 현재 수행되는 쉘이 유지되는 동안만 유효(사실 더 정확하게는 환경변수를 설정한 프로세스가 유지되는 동안 유효)합니다. 무슨 말이냐구요?

3. 환경변수의 특성

계속 주구장창 현재 쉘, 현재 쉘이라는 말을 썼는데요. 여러분들이 놓치실까봐 초록색으로 표시했습니다. 그림으로 보면 더욱 이해가 빠르실텐데요.  

처음 로그인 쉘인 (1) /bin/bash에서 export를 이용해서 HELLO를 환경 변수로 등록하고 sh로 새로운 쉘을 시작합니다. 이때 (2)/bin/sh로 실행되는 환경이 바뀌게 됩니다. 여기서도 export를 이용해서 WORLD를 환경 변수로 등록하고 다시 새로운 (3)bash 쉘을 실행시켜서 실행 환경을 바꿉니다. 여기서 BYE라는 환경 변수를 등록합니다.

그러면 맨 오른쪽 (3)bash 쉘을 사용하고 있을때 HELLO, WORLD, BYE라는 환경변수는 다 살아있을까요? , 살아있습니다. 맨처음 (1)/bin/bash, 그 다음 (2)/bin/sh도 실행중이니까요. 이제 exit으로 (3)bash를 끝내게 되어도 세 환경 변수 HELLO, WORLD, BYE는 살아있을까요? BYE(3)bashexit을 종료했기 때문에 더 이상 존재하지 않습니다. 그래서 HELLO, WORLD만 남아있게 됩니다. (2)/bin/sh를 끝냈을때, 이때 (1)/bin/bash만 수행되는 상태겠죠. 그러면 HELLO만 환경 변수로 유효하게 되는 겁니다.

아래는 테스트 결과입니다. 헷갈리지 않도록 앞에 프롬프트를 추가했습니다. 참고로 전 쉘에서 설정한 변수를 다음 쉘에서 이용하려면 export를 이용해 환경변수로 등록이 되어야 가능합니다. 그러니까 (1)/bin/bash에서 설정한 HELLO(2)/bin/shecho로 출력하려면 export되어있어야 된다는 겁니다.

(1)/bin/bash# export HELLO="hello!"
(1)/bin/bash# sh
(2)/bin/sh# export WORLD="world!"   
(2)/bin/sh# bash
(3)/bin/bash# export BYE="GOOD BYE~"
(3)/bin/bash# echo $HELLO $WORLD $BYE
hello! world! GOOD BYE~
(3)/bin/bash# exit
exit
(2)/bin/sh# echo $HELLO $WORLD $BYE
hello! world!
(2)/bin/sh# exit
(1)/bin/bash# echo $HELLO $WORLD $BYE
hello!

이런 특성을 모른다면 이런 실수 많이들 하실겁니다.  아래는 간단한 bash 쉘 스크립트를 작성한겁니다.

#!/bin/bash

#현재 프로세스 ID 출력 
echo $$

export HELLO="hello "
export WORLD="world!"
export BYE="good bye!"

echo $HELLO $WORLD $BYE

스크립트를 몰라도 맨위의 첫줄은 bash 스크립트를 알려주는 한줄이니까 그냥 그렇게 쓰는구나 묻지도 따지지지도 말고 흡수하시면 됩니다.

#!/bin/bash

그 다음 줄을 좀 눈여겨 봐야하는데, $$는 현재 프로세스의 ID를 의미합니다. 그래서 echo를 통해서 출력하는 것이구요. 그냥 간단히 현재 실행되는 프로그램의 ID라고 생각하세요.

#현재 프로세스 ID 출력 
echo $$

그리고 우리가 배운 export와 명령을 출력하는 echo가 다입니다.  위 스크립트의 의도는 $HELLO, $WORLD, $BYE를 환경 변수로 설정해서 편하게 쓰려는 의도입니다.  실행권한을 주고 실행하면 쉘 스크립트는 잘 실행되지만 스크립트가 끝난 이후 환경 변수를 불러오고자 한다면 이렇게 빈 공백만 출력이 됩니다.

# chmod 777 setmyenv.sh
# ./setmyenv.sh 
6968
hello world! good bye!
# echo $$
3196
# echo $HELLO

# echo $WORLD

# echo $BYE

그런데 제가 명령어를 수행하는 프로그램의 ID3196이고, , ./setmyenv.sh라는 쉘 스크립트가 실행되는 프로그램 ID6968로 다르네요. 결국 이런 상황입니다.

방금 전 언급한 그 상황이 발생했습니다. 그러면 새로운 실행을 낳지 않고, 현재 있는 쉘에서 그대로 스크립트를 수행하면 될 것 같습니다. 그런 목적을 달성할 명령이 바로 source 명령입니다.

 위의 스크립트를 source를 이용해서 실행해봅시다.

# source setmyenv.sh 
3196
hello world! good bye!
# echo $HELLO
hello
# echo $WORLD
world!
r# echo $BYE
good bye!
# echo $$
3196

보세요. 쉘 스크립트가 그대로 3196에서 실행이 되었죠? 그리고 환경 변수가 설정되어서 현재 쉘에서 사용할 수도 있습니다. 우리가 원하는 목적을 달성할 수 있었습니다. source는 쉘 스크립트를 현재 쉘에서 실행하게 해주는 명령어입니다.

4. 환경 변수 함수

이젠 리눅스에서 프로그램이 어떻게 환경 변수를 다룰 수 있는 지 볼까요? 환경 변수를 설정하는 함수는 setenv, putenv가 있으며 환경 변수를 해제하려면 unsetenv함수를 사용할 수 있습니다. 환경 변수의 어떤 값을 읽어올 경우에는 getenv 함수를 이용하면 됩니다.

4.1 setenv, unsetenv

#include <stdlib.h>
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);

setenv 함수는 name에 해당하는 value를 갖는 환경 변수를 설정해줍니다. overwrite1이라면 기존에 환경 변수값이 있을 때 현재 값으로 덮어쓰고, 0이라면 덮어쓰지 않습니다.

unsetenv 함수는 name에 해당하는 환경 변수를 해제합니다.

성공시 0, 실패시 -1을 반환합니다.

4.2 putenv

#include <stdlib.h>
int putenv(char *string);

putenv이름=형식으로 넘겨줌으로써 환경 변수를 설정할 수 있습니다. 이때 무조건 덮어쓰기가 되니 주의하세요.

성공시 0, 실패시 0이 아닌 값을 반환합니다.

4.3 getenv

#include <stdlib.h>
char *getenv(const char *name);

getenv함수는 name에 해당하는 환경변수를 읽어옵니다. 그래서 그 값을 반환하죠. 만약에 해당하는 환경 변수가 없다면 NULL을 반환합니다.

4.4 환경 변수 함수 이용의 예

네 함수(setenv, putenv, getenv unsetenv)를 다 사용하는 예제를 한번 볼까요.

//setenv.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){

        char *name = NULL;
        char *value = NULL;
        char name_val_pair[128]={0,};
        if(argc != 3){
                printf("Usage %s NAME VALUE\n", argv[0]);
                return 1;
        }

        name = argv[1];
        value = argv[2];

        //overwrite 허용
        //setenv(name, value, 1);
        //overwrite 불가
        //setenv(name, value, 0);
        sprintf(name_val_pair, "%s=%s",name,value);
        putenv(name_val_pair);

        printf("%s=%s\n",name,getenv(name));

        unsetenv(name);
}

putenv를 이용해서 환경 변수를 설정해보았습니다. 만약 여러분이  setenv함수를 이용해서 덮어쓸지 말지를 결정할 수도 있습니다. 여기서는 putenv를 통해서 덮어쓰기가 가능한 환경 변수 설정을 하는 것으로 합시다. 다음은 환경 변수 USER를 바꾸는 실행화면입니다. 처음에는 ubuntu로 설정되어있고, 프로그램을 실행하니까 kali로 바뀌었습니다.

그런데 프로그램이 끝난 이후 USER를 확인해보니 ubuntu로 돌아왔네요. 왜 그렇죠!? 용(3 환경변수의 특성)을 잊지 않으셨죠?

# echo $USER
ubuntu
# ./a.out USER kali
USER=kali
# echo $USER
ubuntu

만약 USER가 바껴지는 것을 확인하고 싶다면 system함수로 다른 shell을 실행시켜셔 확인해보세요. system 함수는 다른 명령어를 실행하는 함수로 이해하시면 됩니다.

//setenv.c
//… 생략 … //

int main(int argc, char *argv[]){
	//… 생략 … //
	putenv(name_val_pair);

        printf("%s=%s\n",name,getenv(name));
        system("/bin/sh");

        unsetenv(name);
}
# ./a.out USER kali
USER=kali
# printenv | grep USER    sh쉘로 전환
USER=kali

 

로그인시 환경변수 자동 셋팅(.bashrc), 전체 사용자에 대한 환경 변수 설정(/etc/profile)에 대한 설정 방법은 아주 훌륭하신 블로거님들이 정리를 잘 해놓았으니, 여기서는 설명하지 않겠습니다.

반응형