더 발전된 쉘 2

신준우·2023년 6월 6일
0

시스템 프로그래밍

목록 보기
11/12

이전 내용 복습

  • if, then, fi는 쉘 스크립트에서 사용되는 제어구조를 나타내는 키워드

  • if는 조건문의 시작, 조건을 평가하여 해당 조건이 참인 겨웅에만 다음 then 블록의 명령어를 실행

  • then에는 실제로 실행시킬 명령어가 들어간다. if의 조건이 참일 경우 실행됨

  • fi 키워드는 조건문의 종료를 나타낸다, if조건문이 끝나는 지점을 표시해줌

  • smsh2.c를 통해서 단순히 명령어와 프로그램을 실행하고 프로세스를 복제하는 쉘이 아닌 좀 더 발전된 쉘을 만들어 보았었다

  • 이번에는 백그라운드 처리를 하는 쉘을 만들어보자

쉘 변수

우선 백그라운드 처리에 대해 생각해보려면 쉘의 로컬 변수와 전역 변수에 대해 알아보아야한다

  • 로컬 변수: 사용자와 터미널 내에서만 작동한다
  • 전역 변수: 쉘의 모든 자식 프로세스에 접근 가능
    - 몇가지 전역 변수에 대해 알아보자!
    OperationSyntaxNotes
    assignmentvar=valueNO spaces
    reference$vare.g. $age
    deleteunset vare.g.unset age
    stdin inputread vare.g.read name
    list varssete.g. set (shows you defined variables in the current shell)
    make globalexport vare.g.export PATH=/usr/bin:/usr/local/bin
  • 쉘은 이런 전역변수들을 어떻게 기억하고 있는걸까?
    - 쉘은 이런 변수의 이름과 변수값들을 저장하려면 공간이 필요하다
    - 이 저장 시스템은 지역변수와 전역 변수를 구분해야 한다
  • 저장 시스템의 가능한 모델은 어떤 형식일까?
VariableValueGlobal?
data"phonebook.dat"n
HOME/home/jamsuy
TERM"t1061"y

대충 이런식으로 표현된다!

인터페이스를 더 뜯어보자
- VLstore(char var, char val): var=val만큼 추가하고 바꾼다
- VLlookup(char* var): var에 대한 값을 검색한다
- VList : stdout에 리스트 업 한다
- 테이블의 자료구조는 어떻게 될까?
- linked list일수도 있고, hash table일수도 있고, tree일수도 있지만, 우린 array of structs를 사용할것이다

변수 커맨드를 추가하는법: Built-ins

우리는 assign, list, retrieve 명령어를 우리의 쉘에 추가해야한다

그러나 다행히! 우리는 이런 작업을 할 수는 있다

  • set: 우리의 쉘에 전달되는 "명령어" (쉘이 구동중인 프로그램을 얘기하는게 아니다!)
    - set은 ls같은 기존 명령어와는 다르다
  • set 명령어는 shell이 exec으로 실행하는 명령어와 구별하기 위해 내장 명령어로 처리되어야 한다

자, 이제 본격적으로 Built-in을 추가한 smsh3라는 코드를 살펴보자!

smsh3

builtin.c

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "smsh.h"
#include "varlib.h"

int assign(char *);
int okname(char *);

int builtin_command(char** args, int* resultp){
		int rv = 0;
		if(strcmp(args[0], "set") == 0){		//args[0]에 입력된 값이 set라는 문자와 같을때
			VLlist();		//VLlist()를 호출하여 현재 설정된 모든 변수 출력
			*resultp = 0;		//resultp를 0으로 설정
			rv = 1;		//rv를 1로 설정, 내장 명령어로 처리됨을 나타냄
		}
		else if(strchr(args[0], '=') != NULL){		//args[0]에 '='문자가 포함된 경우
			*resultp = assign(args[0]);		//resultp에 assign함수를 호출해서 변수 할당
			if(*resultp != -1){		//만약 할당 결과가 -1이 아닐경우
				rv = 1;		//rv를 1로 설정, 내장 명령어로 처리됨을 나타냄
			}
		}
		else if(strcmp(args[0], "export") == 0){		//args[0]이 export인 경우
			if(args[1] != NULL && okname(args[1])){
            //asrgs[1]이 NULL이 아니거나, okname(args[1])이 true인 경우
				*resultp = VLexport(args[1]);
                //VLexport(args[1])을 호출하여 변수를 환경변수로 내보내고
                //결과는 resultp에 저장 
			}
			else{
				*resultp = 1;		//*resultp를 1로 설정
			}
			rv = 1;		//마지막으로 rv를 1로 설정
		}
		return rv;		//rv값 반환
}

int assign(char* str){		//assign 함수를 정의
	char*	cp;
	int	rv;
	
	cp = strchr(str, '=');		//문자열 str에서 '='이라는 문자를 찾는다 
    							//string.h
	*cp = '\0';		//'='문자를 '\0'으로 변경하여 변수 이름과 값을 분리
	rv = (okname(str) ? VLstore(str, cp+1) : -1);
    //oknames함수를 호출하여 변수 이름이 유효한지 확인
    //VLstore(str, cp+1)를 호출하여 변수를 저장
	*cp = '=';		//'='문자를 다시 원래의 값으로 변경
	return rv;		// 변수의 할당 결과인 rv를 반환
}

int okname(char* str){
	char*	cp;
	for(cp = str; *cp; cp++){	//반복문 사용, cp값을 증가시키며 가는거임
		if((isdigit(*cp) && cp==str)||!(isalnum(*cp)||*cp=='_')){
        //그렇지 않은경우 조건을 확인하여 알파벳 또는 숫자 또는 '_'문자인지 확인
			return 0;
		}
	}
	return (cp != str)
}

자, 우리는 builtin command를 만들려고하고있다
여기선 builtin_command, assign, okname 함수가 정의되어있다

각각의 전반적 기능을 알아볼까?

  • builtin_command 함수: str이 유효한 변수 이름인지 검사해주는 함수
  • assign 함수: str에 대해 변수 이름과 값을 분리, 유효한 변수 이름인 경우, 변수에 값을 할당
    문자열에서 변수 이름과 값을 분리, 유효한 변수 이름인 경우 변수에 값을 할당하는 기능
  • okname 함수: str이 유효한 변수 이름인지를 확인하는 함수

varlib.h

int VLstore(char* name, char* val);

char* VLlookup(char* name);

int VLexport(char*name);

void VLlist();

int VLenviron2table(char* env[]);

char** VLtable2environ();

새롭게 정의된 헤더파일이다
이런 함수가 있다~ 정도만 알고 넘어가자

varlib.c

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

#define MAXVARS 200

struct var{
	char*	str;
	int	global;
};
static struct var tab[MAXVARS];

static char *new_string(char*, char*);
static struct var* find_item(char*, int);

int VLstore(char* name, char* val){		
	struct var *itemp;
	char	*s;
	int rv = 1;
	
	if((itemp=find_item(name, 1)) != NULL && (s=new_string(name, val))!=NULL){
    //find_item함수를 사용해 변수이름 name과 일치하는 변수 항목을 찾는다
    //변수 항목이 존재하며, new_string(name, val)함수를 호출해서 새로운 문자열을 생성
		if(itemp->str)
        //생셩된 문자열이 유효하다면?
			free(itemp->str);		//문자열 해제
		itemp->str = s;		//새로운 문자열 할당
		rv = 0;		//rv값에 0 대입
	}
	return rv;		//rv값을 반환
}

char* new_string(char* name, char* val){		//new_string함수 정의
	char*	retval;		
	retval = malloc(strlen(name)+strlen(val)+2);
    // 동적배열로 retval배열 생성
	if(retval != NULL)		//retval이 NULL이 아닌 경우,
		sprintf(retval, "%s=%s", name, val);		// name과 val을 이름=값 형식으로 조합하여 retval 저장
	return retval;		//retval 반환
}

char* VLlookup(char* name){
	struct var*	itemp;
	if((itemp = find_item(name, 0)) != NULL){		//struct var 구조체를 가리키는 포인터
		return itemp->str+1+strlen(name);		//검색된 변수의 str 멤버를 가리키는 포인터에
        										//1+strlen(name)을 더한 값을 반환
                                                //값 부분을 가리키는 포인터 반환
	}
}

int VLexport(char* name){		//반환값은 성공 여부를 나타내는 정수
	struct var* itemp;		
	int rv = 1;		//rv는 반환 값을 저장하는 변수. 초기값은 1
	
	if((itemp = find_item(name, 0)) != NULL){	//find_item함수를 사용해서 name에 해당하는 변수 검색
    										//검색결과가 NULL이 아닌경우 조건이 참이 된다
		itemp->global = 1;		//itemp->global 멤버를 1로 설정하여 전역 변수로 설정
		rv = 0;		//rv값, 즉 반환값을 0으로 설정해서 성공했음을 나타냄
	}
	else if(VLstore(name, "") == 1){		//name에 빈 값할당, 반환값이 1인경우 조건이 참
		rv = VLexport(name);		//VLexport함수를 재귀적으로 호출해서 변수를 export
        							//변수가 없는 경우에도 export가능하게 됨
	}
	return rv;		//반환 값을 rv로 반환
}

static struct var* find_item(char* name, int first_blank){
	int	i;		//반복문에서 사용할 변수 i 선언
	int	len = strlen(name);		//strlen을 사용해서 name문자열의 길이를 len에 저장
	char	*s;		//s라는 포인터 변수 선언
	for(i = 0; i < MAXVARS && tab[i].str != NULL; i++){
    // MAXVARS값만큼 i값이증가, tab[i].str값이 비어있지 않을때
		s = tab[i].str;		//tab[i].str값을 s에 저장
		if(strncmp(s, name, len) == 0 && s[len] == '='){
        //s와 name을 len만큼 비교, 비교 결과가 0인경우, s[len]이 =인 경우 조건이 참이 된다
			return &tab[i];		//tab배열의 i번째 요소의 주소를 반환, 해당 값을 찾았음을 표시
		}
	}
	if(i < MAXVARS && first_blank){		
    //변수 검색을 마쳤는데도 i가 MAXVARS보다 작고 firstblank가 참인 경우
		return &tab[i];		//tab배열의 i번째 요소의 주소를 반환
	}
	return NULL;		//위의 조건에 해당하지 않으면, NULL을 반환해서 변수를 못찾았음을 나타냄
}

void VLlist(){
	int i;		//반복문에서 사용할 변수
	for(i = 0; i < MAXVARS && tab[i].str != NULL; i++){
    //tab 배열의 순회
		if(tab[i].global){		//tab[i]의 global 멤버가 참인 경우
			printf("	*%s\n", tab[i].str);		//tab[i].str을 출력, *으로 글로벌 변수임을 나타냄
		}
		else{		//참이 아닌경우
			printf("	 %s\n", tab[i].str);		//tab[i].str을 출력
		}
	}
}

int VLenviron2table(char* env[]){
	int i;		//반복문에서 사용할 변수
	char* newstring;		//새로운 문자열 포인터
	for(i = 0; env[i] != NULL; i++){		//env[i]배열을 증가시키면서 NULL값이 찾아질때까지
    										//즉, 종료될때까지
		if(i == MAXVARS){		//만약 i값이 MAXVARS와 같다면,
			return 0;		//프로그램 종료
		}
		newstring = malloc(1+strlen(env[i]));		//newstring이라는 배열을 동적배열로 설정
		if(newstring == NULL){		//newstring이 비어있으면
			return 0;		//프로그램 종료
		}
		strcpy(newstring, env[i]);		//env[i]번째 문자열이 newstring과 같다면
		tab[i].str = newstring;		//newstring값을 tab[i].str에 할당
		tab[i].global = 1;		//tab배열의 i번째 요소의 global 멤버를 1로 설정
	}
	
	while(i < MAXVARS){		//i값이 MAXVARS값보다 작은 동안 반복
		tab[i].str = NULL;		//tab의 i번째 요소의 str멤버를 NULL로 설정
		tab[i++].global = 0;		//tab배열의 i번째 요소의 global멤버를 0으로 설정, i를 증가
	}
	return 1;		//1을 반환하여 성공을 나타낸다
}

char** VLtable2environ(){
	int i, j;		//반복문에서 사용될 번수
	int n = 0;		//n을 0으로 초기화
	char** envtab;		//envtab 이중포인터 선언
	
	for(i = 0; i < MAXVARS && tab[i].str != NULL; i++){
    //반복문 사용, tab배열 순회, i가 MAXVARS보다 작고, tab[i].str이 NULL이 아닌 동안 반복
		if(tab[i].global == 1){
        //tab[i]의 global 멤버가 1인경우
			n++;		//n값을 증가
		}
	}
	
	envtab = (char**)malloc((n+1)*sizeof(char*));		//동적배열 설정
	if(envtab == NULL){		//envtab이 비어있는 경우
		return NULL;		//NULL값 반환, 함수 종료
	}
	for(i=0, j=0; i < MAXVARS && tab[i].str != NULL; i++){
    // tab배열을 순회, tab[i].str이 NULL이 아닐떄까지 반복
		if(tab[i].global == 1){		//tab[i]의 global 멤버가 1인경우
			envtab[j++] = tab[i].str;	//envtab을 증가시키며 tab[i].str의 요소를 순차적으로 넣음
		}
	}
	envtab[j] = NULL;		//마지막 요소에 NULL할당
	return envtab;		//envtab을 반환
}
  • VLstore: 주어진 변수 이름과 값을 이용하여 변수를 저장. 변수 항목이 존재하고 새로운 문자열이 유효하게 생성되면, 기존 변수 값이 갱신되고, 반환값이 0 이 됨. 그렇지 않은 경우, 반환값은 1
  • new_string: name과 val을 조합하여 "이름=값"형식의 새로운 문자열을 생성, 해당 문자열을 가리기는 포인터를 반환
  • VLookup: 주어진 name에 해당하는 변수를 검색, 검색된 변수의 값을 가리키는 포인터를 반환
  • VLexport: name에 해당하는 변수를 export하고, export가 성공했는지 여부를 반환
  • first_blank: name에 해당하는 변수를 tab 배열에서 검색해서 해당 변수를 가리키는 포인터를 반환, first_blank가 참인 경우, 새로운 변수를 할당할 공간을 나타내는 포인터 반환
  • VLlist: tab 배열의 내용을 출력하는 함수, global 멤버가 참인경우, *을 붙여서 글로벌 변수임을 표시, 그렇지 않은경우 공백을 붙여 출력
  • VLenviron2table: 전역변수 배열 env를 tab 배열로 변환, 전역변수값을 newstring에 복사, tab 배열의 각 요소에 할당. tab 배열 요소를 초기화
  • VLtable2environ: tab 배열에서 global 멤버가 1인 요소들을 추출하여 이중 포인터 envtab에 저장하는 함수. 반환된 envtab은 환경 변수를 나타내는 char*배열로 사용가능

결과

점점 갈수록 코드가 복잡해진다...
그러나 아직 코드를 한번 더 수정해볼 수 있다...!

다음시간에 알아보자

profile
보안

0개의 댓글