즉시 실행 함수 표현(IIFE, Immediately Invoked Function Expression)
용어 그대로 즉시 실행되는 함수 표현식입니다.
다음은 MDN에 소개되어 있는 예제입니다.
(function () {
var aName = "Barry";
})();
// IIFE 내부에서 정의된 변수는 외부 범위에서 접근이 불가능하다.
aName // throws "Uncaught ReferenceError: aName is not defined"
올바르게 사용하기 위해서는 다음과 같이 해야 합니다.
var result = (function () {
var name = "Barry";
return name;
})();
// 즉시 결과를 생성한다.
result; // "Barry"
즉, IIFE는 내부에서 선언된 변수는 해당 함수내에서만 사용 할 수 있고 외부에서는 접근이 불가능한 javascript의 scope 특성을 이용한 Self-Executing Anonymous Function 으로 알려진 디자인 패턴입니다.
그렇다면 IIFE 외부에서 선언된 변수를 IIFE 내부에서 접근하는 것은 어떨까요
다음 코드는 IIFE 외부에 변수 a를 선언 및 초기화하였고 IIFE를 변수 f2에 할당한 코드입니다.
const a = 1;
const f2 = (function() {
return ++a
})()
console.log(f2);
console.log(f2);
console.log(f2);
“Uncaught TypeError: Assignment to constant variable.”
IIFE에서 a에 다시 값을 할당하려고 했기때문에 오류가 발생하였습니다.
a에 접근은 가능한 것 같습니다.
그러면 변수 a를 재할당이 가능한 let이나, 좀 더 유연한 var로 선언하면 가능하겠네요
var a = 1;
const f2 = (function() {
return ++a
})()
console.log(f2);
console.log(f2);
console.log(f2);
2
2
2
흠…변수 a의 값에 정상적으로 접근은 한것 같은데,,예상했던 것과는 조금 다른 결과가 나왔습니다.
제가 원했던 코드는
var a = 1;
const f2 = (function() {
return ++a;
})
console.log(f2());
console.log(f2());
console.log(f2())
2
3
4
와 같은 결과가 나오길 원했는데,,,
IIFE 함수를 변수에 저장하고 이를 호출할때 마다 함수가 실행되었으면 싶은데..
머가 문제지…
f2에 동일한 익명함수를 할당한거 같은ㄷ…아… 같은 함수가 아니구나…!!
IIFE는 즉시 실행되는 함수이고, f2에는 그 결과가 저장된 것이니
const f2 = function() { return 2++ }
이 아니라
const f2 = 2
라는 것을 이틀만에 깨달았습니다.
지금 생각해보면 너무나도 당연한거라 작성하던 포스팅을 지워야 하나 싶었지만,,삽질이라는 기록으로 남기고자 합니다.
Unrecognized error number: 8192: Function create_function() is deprecated
Error를 발생 시킨 create_function()은 PHP 7.2부터 제거된 함수입니다.
Warning : This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.
phpldapadmin의 official website에서는 최신버전을 sourceforege에서 다운로드 하라고
안내하고 있습니다.
하지만 sourcefore에는 2012-10-01 이후로 업데이트 내역(2020-03-26 기준)이 없습니다.
(php의 버전을 낮추는 것도 한 방법입니다…?)
혹시나 싶어서 github에 검색을 해보니 phpldapadmin 수정본(?)이 있었고, 그 중 leenoks님의 phpLDAPadmin 을 재설치하였습니다.
#apt remove phpldapadmin
#git clone https://github.com/leenooks/phpLDAPadmin
#cp ./phpLDAPadmin/config/config.php.example ./phpLDAPadmin/conf/config.php
#vi ./phpLDAPadmin/config/config.php
#mv phpLDAPadmin /var/www/html/phpLDAPadmin
#service apache2 restart
: generate new password, optionally apply it to a user
비밀번호를 생성하여 사용자 계정에 자동으로 적용합니다.[원문]
아무런 매개 변수가 없으면 단순히 비밀번호를 생성하고 이를 표출합니다.
#expect_mkpassword
사용자 계정명을 매개변수로 전달하면, 비밀번호를 생성하고, 이를 해당 계정에 적용합니다.
#expect_mkpassword [userId]
#expect_mkpasswd -l 15 -d 3 -c 3 -C 3 -s 3
: generate an Expect script from watching a session 자동으로 Expect script를 생성합니다.[원문]
#expect_autoexpect
autoexpect 실행 후 해당 쉘 종료 시 expect script가 생성됩니다.
#expect_autoexpect [Target Program]
autoexpect 실행 후 해당 프로그램 종료 시 expect script가 생성됩니다.
#!/usr/bin/expect -f
#
# This Expect script was generated by autoexpect on Tue Mar 17 11:29:00 2020
# Expect and autoexpect were both written by Don Libes, NIST.
#
# Note that autoexpect does not guarantee a working script. It
# necessarily has to guess about certain things. Two reasons a script
# might fail are:
#
# 1) timing - A surprising number of programs (rn, ksh, zsh, telnet,
# etc.) and devices discard or ignore keystrokes that arrive "too
# quickly" after prompts. If you find your new script hanging up at
# one spot, try adding a short sleep just before the previous send.
# Setting "force_conservative" to 1 (see below) makes Expect do this
# automatically - pausing briefly before sending each character. This
# pacifies every program I know of. The -c flag makes the script do
# this in the first place. The -C flag allows you to define a
# character to toggle this mode off and on.
set force_conservative 0 ;# set to 1 to force conservative mode even if
;# script wasn't run conservatively originally
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
#
# 2) differing output - Some programs produce different output each time
# they run. The "date" command is an obvious example. Another is
# ftp, if it produces throughput statistics at the end of a file
# transfer. If this causes a problem, delete these patterns or replace
# them with wildcards. An alternative is to use the -p flag (for
# "prompt") which makes Expect only look for the last line of output
# (i.e., the prompt). The -P flag allows you to define a character to
# toggle this mode off and on.
#
# Read the man page for more info.
#
# -Don
set timeout -1
spawn $env(SHELL)
match_max 100000
expect -exact "\]0;gmpark@DESKTOP-IMN1RJ9: ~\[01;32mgmpark@DESKTOP-IMN1RJ9\[00m:\[01;34m~\[00m\$ "
send -- "ls -al\r"
expect -exact "ls -al\r
total 100\r
drwxr-xr-x 1 gmpark gmpark 512 Mar 17 11:29 \[0m\[01;34m.\[0m\r
drwxr-xr-x 1 root root 512 Sep 26 14:16 \[01;34m..\[0m\r
-rw-r--r-- 1 gmpark gmpark 15654 Mar 16 20:12 .bash_history\r
-rw-r--r-- 1 gmpark gmpark 220 Sep 26 14:16 .bash_logout\r
-rw-r--r-- 1 gmpark gmpark 3772 Nov 15 14:39 .bashrc\r
drwxr-xr-x 1 gmpark gmpark 512 Mar 16 16:25 \[01;34m.cache\[0m\r
drwxr-xr-x 1 gmpark gmpark 512 Mar 16 15:22 \[01;34m.cpan\[0m\r
drwxr-xr-x 1 gmpark gmpark 512 Nov 5 18:41 \[01;34m.gem\[0m\r
-rw-r--r-- 1 gmpark gmpark 129 Mar 16 16:25 .gitconfig\r
drwxr-xr-x 1 gmpark gmpark 512 Nov 5 19:06 \[01;34m.local\[0m\r
-rw-r--r-- 1 gmpark gmpark 807 Sep 26 14:16 .profile\r
drwxr-xr-x 1 gmpark gmpark 512 Oct 14 17:14 \[01;34m.ssh\[0m\r
-rw-r--r-- 1 gmpark gmpark 0 Nov 5 18:08 .sudo_as_admin_successful\r
drwxr-xr-x 1 gmpark gmpark 512 Nov 13 18:29 \[01;34m.vim\[0m\r
-rw-r--r-- 1 gmpark gmpark 34711 Mar 17 11:09 .viminfo\r
-rw-r--r-- 1 gmpark gmpark 9990 Nov 15 17:54 2019-11-14-type-of-certification.md\r
drwxr-xr-x 1 gmpark gmpark 512 Mar 16 15:04 \[01;34mexampleCode\[0m\r
drwxr-xr-x 1 gmpark gmpark 512 Mar 17 10:24 \[01;34mgwimong.github.io\[0m\r
-rwxr-xr-x 1 gmpark gmpark 0 Mar 17 11:29 \[01;32mscript.exp\[0m\r
\]0;gmpark@DESKTOP-IMN1RJ9: ~\[01;32mgmpark@DESKTOP-IMN1RJ9\[00m:\[01;34m~\[00m\$ "
send -- ""
expect eof
Perl Expect로 좀 더 범용적으로 사용 할 수 있도록 구현하였습니다.
#!/usr/bin/perl
use strict;
use Switch;
use Expect;
#$Expect::Multiline_Matching = 0;
use constant {
# connection type
CONN_UNKNOWN => 0,
CONN_TELNET => 1,
CONN_SSH => 2,
# command control
EC_SUCCESS => 0,
EC_ERROR => 1,
EC_SVR_WRNG_ACCOUNT => 2,
EC_CONN_FAILED => 3,
EC_SCRIPT_LOAD_FAILED => 4,
EC_SCRIPT_DUPLICATED => 5,
EC_TIMEOUT => 6,
# command type
RUN_COMMAND => 1,
RUN_FILE => 2,
# connect count
MAX_CONNECT_COUNT => 3
};
my $timeout = 30;
my $root_prompt = '[#] $';
my $host = "localhost";
my $port = 22;
my $conn_mode = 2;
my $account = "";
my $passwd = "";
my $root_passwd = "";
my $su_change_str = "su";
my $command_type = RUN_COMMAND;
my $connect_type = CONN_SSH;
my $conn;
my $command_str;
my $command_file;
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime;
my $log_postfix = sprintf("%04d%02d%02d_%02d%02d%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec).".log";
&main();
sub main {
my $exp = new Expect;
my $is_error = "0";
$exp->raw_pty(0);
$exp->log_stdout(1);
$exp->exp_internal(1);
&init();
&connect_server($exp);
&su_change($exp);
$exp->log_file($log_postfix);
switch ( $command_type ) {
case RUN_COMMAND { $is_error = &command_run($exp); }
case RUN_FILE { $is_error = &file_run($exp); }
else { &exit_proc("Unsupported command_type mode", EC_ERROR, 0, undef); }
}
if($is_error) {
&exit_proc("An exit code is not euqal to 0 when executing the command", EC_ERROR, 1, $exp);
}
&exit_proc("Completed all tasks successfully", EC_SUCCESS, 1, $exp);
}
sub init {
if ( $#ARGV < 1 ) {
&exit_proc("Not enough arguments. Please check it again", EC_ERROR, 0, undef);
}
for(my $i = 0; $i < $#ARGV; $i++) {
switch($ARGV[$i]) {
case "-T" { $conn_mode = CONN_TELNET; }
case "-S" { $conn_mode = CONN_SSH; }
case "-C" {
$command_type = RUN_COMMAND;
$command_str = $ARGV[++$i];
}
case "-F" {
$command_type = RUN_FILE;
$command_file = $ARGV[++$i];
}
case "-h" { $host = $ARGV[++$i]; }
case "-rp" { $root_passwd = $ARGV[++$i]; }
case "-u" { $account = $ARGV[++$i]; }
case "-p" { $passwd = $ARGV[++$i]; }
case "-P" { $port = $ARGV[++$i]; }
}
}
print "HOST : $host\n";
print "PORT : $port\n";
print "Connction mode(SSH(1), Telnet(2)) : $conn_mode\n";
print "Connection account : $account\n";
print "Connection account password : $passwd\n";
print "Target's root password : $root_passwd\n";
print "Command type(Commamd line(1), Input File(2)) : $command_type\n";
if( $command_type == RUN_COMMAND) {
print "Command : \n$command_str\n";
} else {
print "Command file path : $command_file\n";
}
return;
}
sub exit_proc {
my ($msg, $code, $do_release, $sub) = @_;
if($do_release && defined($sub)) {
$sub->send("exit\n");
$sub->soft_close();
}
printf "Exit Message = $msg\nExit Code = $code\nBye.\n";
exit $code;
}
sub timeout_err {
&exit_proc("Given execution time exceeded", EC_TIMEOUT, 0, undef);
return;
}
sub connect_server {
my $exp = shift;
my $connect_count = 0;
switch ( $conn_mode ) {
case CONN_TELNET { $conn = "telnet ".$host." ".$port; }
case CONN_SSH { $conn = "ssh ".$account."@".$host." -p ".$port; }
else { &exit_porc("Unsupported connection mode", EC_ERROR, 0, undef); }
}
$exp->spawn($conn);
$exp->expect($timeout,
[qr "sername: " => sub { my $sub = shift; $sub->send("$account\n"); exp_continue; } ],
[qr "ssword: " => sub {
my $sub = shift;
$sub->send("$passwd\n");
$connect_count = $connect_count +1;
if( $connect_count > MAX_CONNECT_COUNT ) {
&exit_proc("Failed to login", EC_SVR_WRNG_ACCOUNT, 0, undef);
}
exp_continue;
}],
[qr "yes/no" => sub{ my $sub = shift; $sub->send("yes\n"); exp_continue; }],
[qr "[y]:" => sub{ my $sub = shift; printf "???\n"; $sub->send("y\n"); exp_continue; }],
[qr "ermission denied|onnection refused" => sub{
&exit_proc("Failed to login", EC_SVR_WRNG_ACCOUNT, 0, undef);
}],
[qr "ogin incorrect|onnection closed" => sub{&exit_proc("Failed to login", EC_SVR_WRNG_ACCOUNT, 0, undef);}],
[qr "o route to host" => sub{&exit_proc("No route to host", EC_CONN_FAILED, 0, undef);}],
[timeout => \&timeout_err],
"-re", qr"[#>\$\]]\s?(\e\[\d*;?\d*m){0,1}\s?$");
return;
}
sub su_change {
my $exp = shift;
if( $root_passwd ne "" ) {
my $changeenv = 0;
$exp->send("$su_change_str\n");
$exp->expect($timeout,
[qr "ssword: $|ssword:$" => sub{
my $sub = shift;
$sub->send("$root_passwd\n");
exp_continue;
}],
[qr "\S\S: $|\S\S:$" => sub{
my $sub = shift;
$sub->send("$root_passwd\n");
exp_continue;
}],
[qr "incorrect password" => sub{ $changeenv = 1; return;}],
[qr "su: Sorry" => sub{ $changeenv = 1; return;}],
[timeout => \&timeout_err],
"-re", qr "$root_prompt");
# if changing succeed check a su_changed
if($changeenv == 1) {
&exit_proc("Failed to su change.", EC_SVR_WRNG_ACCOUNT, 1, $exp);
}
return EC_SUCCESS;
}
sub command_run {
my ($exp) = @_;
my @splitted_cmd = split(/;/, $command_str);
$exp->log_file($log_postfix, "w");
for(my $i = 0; $i <= @splitted_cmd; $i++ ) {
my $struct_cmd = $splitted_cmd[$i];
$exp->send("$struct_cmd\n");
sleep(1);
$exp->log_file();
$exp->expect(2, [qr "--More--" => sub{
$exp->send(" ");
exp_continue;
}],
"-re", qr "$root_prompt");
}
sleep(1);
return EC_SUCCESS;
}
sub file_run {
my $exp = shift;
open FILE, $command_file or &exit_proc("Unable to load given $command_file", EC_SCRIPT_LOAD_FAILED, 1, $exp);
# read script file and run command
while(my $line = <FILE>) {
chomp($line); # remove line eof("\0")
$line =~s/\t/\cV\t/g;
$exp->send("$line\n");
sleep(1);
$exp->log_file();
$exp->expect(2, [qr "--More--" => sub{
$exp->send(" ");
exp_continue;
}],
"-re", qr "$root_prompt");
}
close(FILE);
return EC_SUCCESS;
}
원격으로 로그인 후 입력된 명령을 수행합니다.
#commandRun.pl -h [host] -u [userId] -p [userPassword] -rp [host's root password] -C [Command String]
Perl Expect의 다양항 모듈 중 SSH 로그인을 위한 모듈을 소개드립니다.
간단하게 SSH 로그인하여 원격으로 명령을 수행하는 스크립트를 구현 할 수 있습니다.
: SSH wrapper to execute remote commands
SSH를 이용한 원격 명령 실행 모듈입니다.[출처]
cpan을 이용하여 설치합니다.
# sudo cpan Net::SSH::Expect
#!/usr/bin/perl
use Net::SSH::Expect;
my $ssh = Net::SSH::Expect->new (
host => "localhost",
password=> 'test123',
user => 'test',
raw_pty => 1
);
my $login_output = $ssh->login();
print ($login_output);
if ($login_output !~ /Welcome/) {
die "Login has failed. Login output was $login_output";
}
my $who = $ssh->exec("who");
print ($who);
my $line;
while ( defined ($line = $ssh->read_line()) ) {
print $line . "\n";
}
print $ssh->peek(0); # you'll probably see the remote prompt
$ssh->eat($ssh->peek(0)); # removes whatever is on the input stream now
my $chunk;
while ($chunk = $ssh->peek(1)) { # grabs chunks of output each 1 second
print $ssh->eat($chunk);
}
$ssh->close();