직접 DB연결 없이 Shell command로 해결하기
PRO*C연동 없이 DB핸들링 하려다가, 하다보니 모든걸 Shell로 만들어놓고 그 쉘을 실행시키서 리턴을 받으면 좋겠다는 생각이 들었다.
터미널에서 Oracle SQL을 실행하는 Shell이다.
파일명은 exex.sql
----------------- SOURCE START ----------------------
USER=user
ORACLE_BASE=/export/home/oracle
ORACLE_SID=ORA9
ORACLE_HOME=/export/home/oracle/app/oracle/product/902
ORA_NLS33=/export/home/oracle/app/oracle/product/902/ocommon/nls/admin/data
LD_LIBRARY_PATH=/export/home/oracle/app/oracle/product/902/lib
NLS_LANG=AMERICAN_AMERICA.KO16KSC5601
#!/usr/bin/csh
sqlplus -s db_id/db_passwdw@tns_name << EOF
set hea off
set pagesize 0
set tab off
$1
EOF
----------------- SOURCE END ------------------------
이 쉘 파일은 터미널에서 아래와 같이 실행시킨다.
> exec.sql "SELET * FROM table_name;"
뭐 다른 DB는 이런 쉘없이 바로 실행가능하니깐...
MYSQL : mysql < "SELECT * FROM table_name"
INFORMIX : dbaccess < "SELET * FROM table_name"
터미널에서야 이런식으로 끝내버리면 되지만 실행되는 프로그램에서 하려면 각 DB의 스펙에 맞는 Embeded sql 프로그램을 짤수밖에 없다.
그러나 ..
embeded sql 사용없이 쉘처럼 실행하려는 프로그램이 있다면 ...
----------------- SOURCE START ----------------------
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
void sig_chld(int signo)
{
......pid_t pid;
......int stat;
......while( (pid=waitpid(-1, &stat, WNOHANG)) > 0);
......return;
}
int main( int argc, char **argv )
{
......FILE *fp;
......char buf[64],info[20],command[256],param[1024],str[1024];
......int my_pid,child_pid,pid;
......int mypipe[2];
......int size=1,status;
......pid_t childpid, wait_childpid;
......memset(command, 0x00, sizeof(command));
......memset(param, 0x00, sizeof(param));
......memset(str, 0x00, sizeof(str));
......if(argc < 2)
......{
............printf( "Usage : command run [parameter]\n" );
............return 1;
......}
......if(argc >= 2) strcpy(command, argv[1]);
......if(argc >= 3)
......{
............strcpy(param, argv[2]);
............sprintf(str, "%s \"%s\"", command, param);
......}else{
............sprintf(str, "%s", command);
......}
......my_pid = getpid();
......pipe(mypipe);
......if (getpid() != my_pid) exit(0);
......signal(SIGCLD, sig_chld);
......pid = fork();
......if (pid == 0)
......{
............dup2(mypipe[1], 1);
............close(mypipe[0]);
............close(mypipe[1]);
............/*execl( path , command, param, NULL ); */
............system( str );
............if(errno) perror( "child failed to exec ls" );
............exit(0);
......}
......while(size > 0)
......{
............memset(buf, 0x00, sizeof(buf));
............size = read(mypipe[0], buf, sizeof(buf));
............write(1, buf, size);
......}
......return 1;
}
----------------- SOURCE END ------------------------
1. 실행 쉘을 차일드 프로세스를 통해 실행시킨다.
- 단일 프로세스에서 실행시켰을 경우 실행 쉘과 부모 프로세스간의 동기화 문제가 발생한다, 즉 부모 프로세스가 먼저 종료될 경우 리턴값을 못 받는다는 것이다.
- 처음엔 execl () 함수를 사용했으나, 실제로 system ()와 별차이없는것 같다.
2. 파이프를 통해 차일드의 리턴값을 받는다.
- fork 를 통해 차일드 프로세스를 생성하고 거기서 쉘을 실행시킨다.
- 부모 프로세스를 Read pipe로 부터 리턴값을 전송받는다.
- signal을 반드시 설정해 주어야 한다, 그렇지 않을 경우 차일드 프로세스가 종료됨을 알지 못해서 무한 블러킹 상태가 되버린다.
3. 가장 중요한 장점: 코드 라인수를 보라. 정말 심플하게 짧지 않은가 ㅋㅋ
compile하고 쉘을 실행시켜본다.
> cc -o pipe pipe.c
> pipe shell paramter
or
> pipe exec.sql "SELET * FROM table_name;"