쉘 스크립트로써의 PHP cli
쉘 스크립트로써의 PHP cli#
개요#
리눅스 시스템에서 명령행 기반의 스크립트는 운영체제의 사용을 편리하게 해줍니다. 단 기존의 쉘 스크립트는 (흔히 사용하는) 배쉬 쉘 스크립트나 펄 스크립트나, 파이썬 스크립트등을
이용하고 있으므로, PHPer들에게 있어서 진입장벽이 높았던 것은 사실입니다. (배쉬 쉘이나 펄, 파이썬등을 배워야 했으므로) 옛 버전의 PHP 인터프리터들은 안정성이나 편의성등이 부족하여 쉘 스크립트계(?)에서 외면 당해 온 것이 사실이나, 4.3.0버전 이후로 새로 도입된 PHP cli(PHP Command Line Interface)은 쉘 스크립트를 작성하려는 PHPer의 요구를 충분히 반영하고 있다고 생각합니다.
이에 몇 가지 쉘 스크립트로써의 PHP cli에 대한 내용을 소개할까 합니다.
표준 입/출력/에러#
먼저 표준 입/출력/에러에 대한 내용은 아래의 링크를 참조하시거나 리눅스 레퍼런스북을 참조하시기 바랍니다.
http://docs.hp.com/ko/B2355-90167/ch03s03.html
http://wiki.kldp.org/wiki.php/BashProgIntroHOWTO
PHP cli는 다음의 방법들로 쉽게 각 표준 스트림들을 열게 합니다.
- 방법 1.
$stdin = fopen("php://stdin", "r"); //표준 입력은 읽기 전용입니다.
$stdout = fopen("php://stdout", "w"); //표준 출력은 쓰기 전용입니다.
필요하다면 방법 1을 사용해야겠지만, 간편하게 미리 지정된 상수를 파일 핸들러로 사용할 수 있습니다.
- 방법 2.
표준입력 STDIN
표준출력 STDOUT
명령행 인자 사용#
마치 웹에서 GET이나 POST등을 이용해 스크립트에 정보를 전달하는 것처럼 명령행에서도 PHP 스크립트에 정보를 전달해야 할 필요가 있습니다.
- > php some_script.php first_param second_param
각 인자는 공백으로 구분하므로 하나의 인자는 반드시 공백없이 처리되어야 합니다. 위 명령에서 인자로 주어진 first_param과 second_param은 다음의 변수에 배열로 저장됩니다.
- $argv ($_SERVER['argv']도 같은 내용을 갖습니다.)
이때 배열의 0번 인덱스에는 스크립트 파일명이 저장되고, 인자는 1번 인덱스부터 차례대로 저장됩니다.
- 예제 1.
> php some_script.php first_param second_param
<?
print_r($_SERVER['argv']); //슈퍼전역변수를 사용하는 것은 언제나 좋은 습관입니다. (단, 이놈의 변수명이 버전마다 자주 바뀐다는게 문제지만..)
?>
Array
(
[0] => some_script.php
[1] => first_param
[2] => second_param
)
이렇게 저장된 인자를 이용해 스크립트의 반응을 여러가지로 지정할 수 있습니다.
팁#
PHP 쉘 스크립트를 좀 더 유연하게 만드는 몇가지 팁을 소개합니다.
커맨드 라인에 명시적으로 PHP를 지정하지 않고 스크립트 실행하기#
일반적으로 PHP 쉘 스크립트를 실행하기 위해선 다음과 같은 명령을 내려야 합니다.
- > php some_script.php
매번 이러기는 귀찮지요. 다음과 같은 구문을 some_script.php의 맨 첫줄에 추가하고 실행권한을 줍니다. (chmod 755 some_script.php)
- #!/usr/bin/php
<?
...
?>
> ./some_script.php
배쉬 쉘은 (다른 쉘은 어떤지 모르겠습니다만) 일반적으로 자기 자신이 스크립트를 해석해서 실행하려고 합니다만, 첫줄의 #!/usr/bin/php은 배쉬 쉘에게 이하의 스크립트를 해석할 인터프리터를 지정하도록 합니다. #! 다음의 경로는 시스템에 따라 달라질 수 있습니다.
제한된 인자만 받기#
다음은 메뉴얼에 있는 내용입니다.
- #!/usr/bin/php
<?php
if ($argc != 2 || in_array($argv[1], array('--help', '-help', '-h', '-?'))) {
?>
This is a command line PHP script with one option.
Usage: <?php echo $argv[0]; ?> <option>
<option> can be some word you would like to print out.
With the --help, -help, -h, or -? options,
you can get this help.
<?php
} else {
echo $argv[1];
}
?>
$argc ($_SERVER['argc']) 에는 입력된 인자의 개수가 저장됩니다. if문에서 인자의 개수가 2개가 아니고 (즉, 2개의 인자는 인정하지 않겠다는 의미지요. $argc < 2가 되어야 할텐데, 메뉴얼이 좀 잘못됐네요.), 인자의 내용이 --help, -help, -h, -?이면 현재 스크립트의 사용법을 출력하고, 인자가 하나이면서 in_array에 해당되지 않는 인자면 그대로 출력하라는 의미입니다.
이걸 응용하면 원하는 인자만 받고, 그 외의 경우 Invalid Parameters나 사용법을 출력하게 할 수도 있습니다.
Prefix가 붙은 인자 받기#
어떤 PHP 쉘 스크립트가 파일명과 해당 파일명에 대한 처리 방법을 입력받아 그대로 수행한다고 합시다.
- > some_script.php file_name delete
그러나 여러 다른 스크립트에서 사용자 편의를 위해 인자의 순서를 사용자에게 일임하는 경우가 있습니다.
- > some_script.php file_name delete || > some_script.php delete file_name
그런데 어떤 인자가 파일이고 어떤 인자가 해당 파일에 대한 처리 방법인지 모호합니다. (인자 가운데 정해진 처리 방법, 즉 delete, copy, symlink등을 일일이 체크 할 수도 있습니다만 여기서는 다른 방법으로 처리하겠습니다.) 이때에 명시적으로 인자들 앞에 Prefix를 주도록 유도해서 인자를 명확하게 할 수 있습니다.
- > some_script.php -file file_name -action delete
이 경우 -file의 Prefix에 file_name이 지정된 것을 PHP로 하여금 어떻게 인식하도록 할까요? 다음과 같이 하면 됩니다.
- #!/usr/bin/php
- <?
- for($i = 1; $i <= count($_SERVER['argv']); $i+=2) {
-
switch($_SERVER['argv'][$i]) {
-
case '-file' :
-
$file_name = $_SERVER['argv'][$i + 1];
-
break;
-
case '-action':
-
$action = $_SERVER['argv'][$i + 1];
-
break;
-
}
- }
- echo "file_name => ".$file_name." || action => ".$action."\n";
- ?>
먼저 $i의 초기값이 1인 것은 $_SERVER['argv']의 0번째 인덱스에 스크립트 파일명이 들어가기 때문입니다. 증가값은 2로 합니다. 루프를 돌면서 공백으로 구분된 인자가 '-file'인 경우 그 다음 배열 인덱스에 저장된 인자는 당연히 file_name이므로 $file에 $_SERVER['argv'][$i + 1]의 값을 저장하도록 합니다. 증가값이 2인 이유는 Prefix와 Prefix에 대한 값을 한쌍으로 인식하도록 하기 위함입니다.
다른 방법도 있습니다.
- > some_script.php -f=file_name -a=delete
이 경우 인자의 해석은 다음과 같습니다.
- #!/usr/bin/php
- for($i = 1; $i < count($_SERVER['argv']; $i++) {
-
switch($substr($_SERVER['argv'][$i], 0, 3)) {
-
case '-f=' :
-
$file_name = substr($_SERVER['argv'][$i], 3, strlen($_SERVER['argv'][$i]));
-
break;
-
case '-a=':
-
$action = substr($_SERVER['argv'][$i], 3, strlen($_SERVER['argv'][$i]));
-
break;
-
}
- }
예제를 빠르게 작성하느라 Prefix를 -f나 -a등으로 단순화 시켰습니다만, strpos 함수를 이용하면 Prefix의 길이에 상관없이 설정할 수 있습니다.
프로그레스 바 표현하기#
wget은 http://나 ftp://프로토콜을 사용하는 강력한 명령행 다운로드 프로그램입니다. wget을 통해 어떤 파일을 다운로드 하면 중간에 다운로드 되는 상태를 확인할 수 있습니다.
- > wget http://kirrie.mixnut.co.kr/index.php
--19:31:30-- http://kirrie.mixnut.co.kr/index.php
=> `index.php.1'
Resolving kirrie.mixnut.co.kr... 210.109.103.146
접속 kirrie.mixnut.co.kr|210.109.103.146|:80... 접속됨.
HTTP request sent, awaiting response... 200 OK
Length: 20 [text/html]
100%[====================================>] 20 --.--K/s
19:31:30 (2.53 MB/s) - `index.php.1' saved [20/20]
이런게 PHP 쉘 스크립트에서도 작동한다면 멋지겠지요?
- #!/usr/bin/php
- <?
- $progress_bar = "\r[";
- for($i = 0; $i <= 10; $i++) {
-
$percent = ($i * 10)."%";
-
-
for($j = (9 - $i); $j >= 0; $j--) $space .= " "; //$i값이 증가할수록 줄어드는 공백을 생성하기 위한 구문입니다.
-
$space = str_pad("", (10 - $i), STR_PAD_RIGHT); // str_pad 함수를 잊고 있었네요.
-
//예를 들기 위해서 fwrite와 echo를 이용한 구문을 둘 다 적었습니다.
-
fwrite(STDOUT, $progress_bar.$space."] ".$percent);
-
echo $progress_bar.$space."] ".$percent;
-
$space = "";
-
$progress_bar .= "=";
-
sleep(1);
- }
- echo "\n";
- ?>
> some_script.php
[===========] 100%
>
\r에 주의하시기 바랍니다. 캐리지 리턴을 사용하므로 해서 매 출력마다 열린 라인의 맨 앞으로 이동합니다. 또한 fwrite(STDOUT.. 이나 echo, print 등은 모두 동일하게 표준 출력을 이용합니다. echo가 좀 더 간단하겠지요.
인터렉티브한 명령행 스크립트 작성하기#
매번 스크립트에 인자를 넘겨주기 위해 열심히 키보드를 두드릴 필요가 없습니다. 간단히, 인터렉티브하게 2개의 숫자를 받아서 그 둘을 더하는 스크립트를 생각해 보도록 하겠습니다.
- #!/usr/bin/php
- <?
- echo "Input First Number> ";
- $first_num = fgets(STDIN);
- echo "Input Second Number> ";
- $second_num = fgets(STDIN);
- echo "Plus = ".($first_num + $second_num)."\n";
- ?>
- > some_script.php
- Input First Number> 1<enter>
- Input Second Number> 2<enter>
- Plus = 3
- >
- ※ <enter>는 키보드의 enter키를 누른 상태입니다.
fgets는 아시다시피 열려진 파일 핸들러에서 하나의 라인 (\n로 끝나는 한 라인) 을 읽어옵니다. $stdin = fopen("php://stdin", "r"); 해도 같은 효과를 얻겠지만 간편하게 지정된 상수인 STDIN을 사용하는게 좋겠지요.
첨부 : 간이 mysql client#
간단하게 mysql client를 만들어 봤습니다. 물론 mysql에서 자체 제공하는 것 보다야 좋지 않습니다. 그냥 이런 가능성도 있구나 하고 봐주심 ㄱㅅㄱㅅ.
주의사항#
- 스크립트상의 오류로 에러가 발생하면 꽤나 지저분하게 보입니다. 최대한 에러가 나지 않도록 사전에 핸들링 하는게 필요합니다.
- 파일 관련 함수 사용시는 주의하시기 바랍니다. 웹서버의 모듈로 작동하는 PHP 스크립트는 웹서버의 권한으로 작동합니다. 다만, 쉘 스크립트로써 작동하는 PHP 스크립트는 실행자의 권한으로 실행되므로 root 권한으로 동작시에는 무제한으로 파일을 삭제해버릴지도 모릅니다. (스크립트가 어떤 파일들을 삭제하도록 코딩된 경우)
- 거의 대부분의 PHP 함수를 그대로 사용할 수도 있습니다. DB 접속도 가능하므로 잘하면 커스터마이즈된 명령행 mysql client를 만들 수도 있겠지요.
- 윈도우즈의 경우 동작하지 않는 함수들이 있으므로 주의하시기 바랍니다.
- 윈도우즈의 경우 배치파일을 이용하여 스크립트를 작성할 수 있습니다. 즉, some_script.php가 있다고 할때 some_script.bat파일의 내용은 @c:\php\cli\php.exe some_script.php %1 %2 %3 %4 등으로 하여서 동작할 수 있습니다. (라고 메뉴얼에 나와 있네요.)
History
Last edited on 03/10/2008 23:43 by kirrie
Comments (0)