검색 엔진의 방문이 늘어나고 있군...

Posted
Filed under 프로그램과 명령어/관리와 유지보수
참조 원문 : CentOS system administration using text-based user interfaces

  Curses 라이브러리를 사용하는 dialog를 통해 스크립트에서 나름 GUI 느낌이 나게 스크립트를 짜는 예제입니다. CentOS라면 보통은 dialog가 설치되어 있지 않으므로 yum을 사용해서 설치해야 합니다.

  아래 예제에서는 메뉴, 진행률 막대의 기본 사용법을 볼 수 있습니다.
#!/bin/bash

#The dialog part. By default, the chosen menu item goes to STDERR (2). From there it's redirected to a temp file (/tmp/temp_file) in the script.

/usr/bin/dialog --title "Delete /tmp content" --menu "Delete /tmp content older than:" 10 30 0 1 "1 day" 2 "2 days" 2> /tmp/temp_file

#Get the choice by reading the /tmp/temp_file
option=`cat /tmp/temp_file`

#Remove the temp file
rm -f /tmp/temp_file

#Clear the screen for better readibility
/usr/bin/clear

#Create a function to delete the temp content. It accepts as an argument a number of days. Any content older than this number will be deleted.
function delete_tmp {
    /bin/echo "Deleting files and directories older than $1 day(s) in /tmp"
    /bin/find /tmp/ -type f -mtime +$1 -exec rm {} -rf \;
    #Show a dialog gauge with the progress
    /bin/echo "50" | /usr/bin/dialog --gauge "Deleting files" 10 30 0
    #For better visual effect wait 1 second before continuing
    /bin/sleep 1
    /bin/find /tmp/ -type d -mtime +$1 -exec rm {} -rf \;
    #Show a dialog gauge with the progress again
    /bin/echo "100" | /usr/bin/dialog --gauge "Deleting directories" 10 30 50
    /bin/sleep 1
    #Clear the screen
    /usr/bin/clear
}

#Go through a conditional statement
#First check if $option is not null. Option is null usually when the cancel button from the menu is pressed.
if [ -z $option ]; then
    /bin/echo 'Cancel pressed. Exiting.'
#Check if the first option is chosen
elif [ $option -eq 1 ]; then
    delete_tmp 1
#Check the second option
elif [ $option -eq 2 ]; then
    delete_tmp 2
fi
  실행하면 두 가지 선택사항이 나오는데 첫 번째는 /tmp 디렉토리 내에 있는 파일들 중 최종 수정 시간이 하루 이상 지난 파일들을 삭제하는 것이고, 두 번째는 이틀 이상 지난 파일을 삭제하는 것입니다. 선택하면 진행률 막대도 나오는 것을 볼 수 있습니다. 메뉴와 게이지의 인자로 있는 10과 30은 각각 텍스트 박스의 높이와 너비를 텍스트 문자로 쟨 값입니다. 메뉴에 있는 세 번째 인자는 내부 선택 공간의 높이고 네 번째 인자는 처음에 선택되어 있는 항목의 번호입니다. 게이지의 세 번째 인자는 진행률 막대의 시작지점을 비율 값으로 나타낸 것입니다.

  배포판에 따라 명령어의 경로명이 다를 수 있습니다. 가령 데비안 계열의 경우 find의 기본 위치가 /usr/bin/find 입니다. 이런 문제 때문에 스크립트 윗 부분에서 OS 및 배포판을 감지하여 그에 맞는 경로명을 변수에 보관하거나 which 같은 명령어로 즉석에서 알아내서 변수로 보관한 뒤 명령어를 사용할 때 그 변수를 활용하는 것이 더 좋습니다.

2013/07/16 13:18 2013/07/16 13:18
Posted
Filed under 쉘 스크립트
참조 원문 : How to Write Linux Init Scripts Based on LSB Init Standard

  LSB란 Linux Standard Base의 약자로서 리눅스 재단이 배포판 사이의 차이점을 줄여 다른 배포판으로 이식할 때 드는 비용을 줄이기 위해 만든 표준입니다. Init 스크립트도 표준화된 것들 중 하나입니다.

  Init 스크립트는 소프트웨어나 서비스를 시작하거나 중단할 때 사용합니다. 예를 들어 postgresql 소프트웨어를 사용하고 있다면 /etc/init.d/postgresql 이라는 Init 스크립트를 가지고 있을 것이며 인자로 start, stop, restart, reload, force-reload, status를 사용할 수 있습니다.

  LSB 호환 Init 스크립트는 아래 사항을 지켜야 합니다.
  • 최소한 'start, stop, restart, force-reload, status'를 제공해야 한다.
  • 적절한 exit code를 return해야 한다.
  • 실행 의존성(run-time dependencies)을 문서화해야 한다.
  추가적으로 이 스크립트들은 메시지를 남기기 위해 "log_success_msg"나 "log_failure_msg" 같은 init.d 함수를 사용할 수 있습니다.

  LSB는 /lib/lsb/init-functions에 있는 기본 함수들을 제공합니다. Init 스크립트로 실행되는 프로세스들은 /var/run/ 디렉토리에 프로세스의 pid를 기록하는데 이것은 Init 스크립트가 프로세스의 상태를 조사할 때 큰 도움이 됩니다.

  이제 Init 스크립트를 작성해봅시다. 먼저 아래처럼 헤더를 넣습니다.
### BEGIN INIT INFO
# Provides:          my_daemon
# Required-Start:    postgresql networking
# Required-Stop:     postgresql networking
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: This is a test daemon
# Description:       This is a test daemon
#                    This provides example about how to
#                    write a Init script.
### END INIT INFO
  Provides에는 해당 Init 스크립트가 제공하는 기능(facility=서비스)을 적습니다. 이 이름은 유일해야 합니다.

  Requred-Start에는 이 서비스를 시작하기 전에 작동하고 있어야 하는 일련의 기능들을 적습니다. 이 예제의 경우 postgresql과 networking이 미리 실행되어 있어야 함을 나타내고 있습니다. 기억해야 할 점은 이 'postgresql'가 'Provides: postgresql'이라고 적힌 별도의 Init 스크립트를 가지고 있어야 한다는 것입니다. 이를 통해 부팅 시 의존성을 보장합니다. 'inserv' 명령어나 'update-rc.d' 명령어를 사용할 때 이 의존성을 바탕으로 대상 런레벨 디렉토리에 적절한 순번을 매겨 집어넣게 됩니다.
/etc/rc2.d/S12postgresql
/etc/rc2.d/S03networking
/etc/rc2.d/S13my_daemon
  예를 들어 위와 같은 엔트리가 있을 경우 networking, postgresql, my_daemon 순서로 실행됩니다.

  Required-Stop에는 이 기능을 중단한 후에만 중단할 수 있는 일련의 기능들을 적습니다. 이 예제의 경우 my_daemon이 중단된 후에만 postgresql과 networking 기능을 중단할 수 있습니다.

  Default-Start Default-Stop에는 이 서비스가 시작되거나 중단돼야 하는 런레벨을 정의합니다.

  Short-Description Description에는 해당 기능에 대한 설명을 적습니다. Short-Description에는 한 줄만 적을 수 있으며, Description에는 여러 줄에 걸쳐 적을 수 있습니다.

  아래는 실제 Init 스크립트의 예입니다.
### BEGIN INIT INFO
# Provides:          my_daemon
# Required-Start:    postgresql networking
# Required-Stop:     postgresql networking
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: This is a test daemon
# Description:       This is a test daemon
#                    This provides example about how to
#                    write a Init script.
### END INIT INFO

# Using the lsb functions to perform the operations.
. /lib/lsb/init-functions
# Process name ( For display )
NAME=my-daemon
# Daemon name, where is the actual executable
DAEMON=/home/user1/my_daemon
# pid file for the daemon
PIDFILE=/var/run/my_daemon.pid

# If the daemon is not there, then exit.
test -x $DAEMON || exit 5

case $1 in
 start)
  # Checked the PID file exists and check the actual status of process
  if [ -e $PIDFILE ]; then
   status_of_proc -p $PIDFILE $DAEMON "$NAME process" && status="0" || status="$?"
   # If the status is SUCCESS then don't need to start again.
   if [ $status = "0" ]; then
    exit # Exit
   fi
  fi
  # Start the daemon.
  log_daemon_msg "Starting the process" "$NAME"
  # Start the daemon with the help of start-stop-daemon
  # Log the message appropriately
  if start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON ; then
   log_end_msg 0
  else
   log_end_msg 1
  fi
  ;;
 stop)
  # Stop the daemon.
  if [ -e $PIDFILE ]; then
   status_of_proc -p $PIDFILE $DAEMON "Stoppping the $NAME process" && status="0" || status="$?"
   if [ "$status" = 0 ]; then
    start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
    /bin/rm -rf $PIDFILE
   fi
  else
   log_daemon_msg "$NAME process is not running"
   log_end_msg 0
  fi
  ;;
 restart)
  # Restart the daemon.
  $0 stop && sleep 2 && $0 start
  ;;
 status)
  # Check the status of the process.
  if [ -e $PIDFILE ]; then
   status_of_proc -p $PIDFILE $DAEMON "$NAME process" && exit 0 || exit $?
  else
   log_daemon_msg "$NAME Process is not running"
   log_end_msg 0
  fi
  ;;
 reload)
  # Reload the process. Basically sending some signal to a daemon to reload
  # it configurations.
  if [ -e $PIDFILE ]; then
   start-stop-daemon --stop --signal USR1 --quiet --pidfile $PIDFILE --name $NAME
   log_success_msg "$NAME process reloaded successfully"
  else
   log_failure_msg "$PIDFILE does not exists"
  fi
  ;;
 *)
  # For invalid arguments, print the usage message.
  echo "Usage: $0 {start|stop|restart|reload|status}"
  exit 2
  ;;
esac
  위 스크립트는 LSBInit 스크립트 작성을 위한 템플릿입니다. DAEMON, PIDFILE, NAME 변수, 헤더 등을 적절히 바꿔 사용할 수 있습니다.

  LSB가 제공하는 함수에 대해 더 알고 싶다면 InitScript Functions를 참조하시기 바랍니다.

  Init 스크립트를 작성한 후 자동으로 시작되거나 중단되게 만들려면 아래 형식으로 명령어를 실행합니다.(데비안 계열 기준)
update-rc.d filename defaults
  이 명령어는 지정한 런레벨에 의존성을 고려한 적절한 순번을 가진 'S'와 'K' 엔트리를 추가합니다.
2013/07/02 14:14 2013/07/02 14:14
Posted
Filed under 쉘 스크립트
원문 : http://www.linuxforum.com/threads/backup-all-databases-nightly-w-mysqldump.692/

  당일 날짜로 된 디렉토리를 만들어 그 안에 DB를 백업해 넣는 스크립트입니다.
#!/bin/bash

DB_BACKUP="/backups/mysql_backup/`date +%Y-%m-%d`"
DB_USER="root"
DB_PASSWD="secretttt"
HN=`hostname | awk -F. '{print $1}'`

# Create the backup directory
mkdir -p $DB_BACKUP

# Remove backups older than 10 days
find /backups/mysql_backup/ -maxdepth 1 -type d -mtime +10 -exec rm -rf {} \;

# Option 1: Backup each database on the system using a root username and password
for db in $(mysql --user=$DB_USER --password=$DB_PASSWD -e 'show databases' -s --skip-column-names|grep -vi information_schema);
do mysqldump --user=$DB_USER --password=$DB_PASSWD --opt $db | gzip > "$DB_BACKUP/mysqldump-$HN-$db-$(date +%Y-%m-%d).gz";
done

# Option 2: If you aren't using a root password then comment out option 1 and use this
# for db in $(mysql -e 'show databases' -s --skip-column-names|grep -vi information_schema);
# do mysqldump --opt $db | gzip > "$DB_BACKUP/mysqldump-$HN-$db-$(date +%Y-%m-%d).gz";
# done
  mysql의 root 계정 패스워드가 들어있기 때문에 퍼미션을 700으로 변경 후 cron에 등록하면 됩니다.
2013/06/23 17:03 2013/06/23 17:03
Posted
Filed under 쉘 스크립트
참조 원문 : Log Parser
#!/bin/bash

script=$(basename $0)_errors
log1=/var/log/messages
log2=/var/log/secure
log3=/var/log/dmesg

mydate=$(date +%b\ %d)

for log in $log{1,2,3}
do
if [ -e $log ] && [ -s $log ]
then
echo
echo BEGIN $log
grep -E "$mydate" $log | grep -E 'Device|fail'  2> $script
echo END $log
echo
fi
done
  필요한대로 수정한 후 cron으로 매일 23시 55분에 실행하면 적당할 것 같군요. 변수로 지정한 로그 파일들에서 당일 생성된 항목 중 지정한 키워드(위의 경우 Device나 fail)가 검출되면 해당 라인을 미리 정한 파일에 집어넣습니다. 간단해서 설명할 것도 없군요. 결과는 아래처럼 나오겠지요.
BEGIN /var/log/messages
Aug 31 01:21:43 mail kernel: pnp: Device 00:05 does not support disabling.
Aug 31 01:21:53 mail smartd[2046]: Device: /dev/hda, opened
Aug 31 01:21:53 mail smartd[2046]: Device: /dev/hda, not found in smartd database.
Aug 31 01:21:53 mail smartd[2046]: Device: /dev/hda, lacks SMART capability
Aug 31 01:21:53 mail smartd[2046]: Device: /dev/hda, to proceed anyway, use '-T permissive' Directive.
Aug 31 01:21:53 mail smartd[2046]: Device: /dev/hdc, opened
Aug 31 01:21:53 mail smartd[2046]: Device: /dev/hdc, packet devices [this device CD/DVD] not SMART capable
END /var/log/messages

BEGIN /var/log/secure
Aug 31 01:21:48 mail sshd[1872]: error: Bind to port 22 on 0.0.0.0 failed: Address already in use.
END /var/log/secure

BEGIN /var/log/dmesg
END /var/log/dmesg
  근데 많이 부실하긴 하네요. 나중에 좀 더 견고한 걸 올려야겠습니다.


2013/06/20 17:33 2013/06/20 17:33
Posted
Filed under 쉘 스크립트
가끔 스크립트에서 CPU 사용률을 구해야 할 때가 있습니다. 예를 들어 CPU 사용률을 측정하기 위한 Nagios 플러그인 같은 걸 만들어야 할 때가 있죠. 그럴 때 유용하게 쓸 수 있는 간단한 스크립트입니다.
#!/bin/bash

$INTERVAL_SEC=1
$NUM_REPORT=3

CPU_REPORT=`vmstat -n $INTERVAL_SEC $NUM_REPORT | tail -$NUM_REPORT | awk 'BEGIN{USER=0;SYS=0;WAIT=0;IDLE=0} {USER=USER+$13;SYS=SYS+$14;WAIT=WAIT+$16;IDLE=IDLE+$15} END{print USER/'$NUM_REPORT'" "SYS/'$NUM_REPORT'" "WAIT/'$NUM_REPORT'" "IDLE/'$NUM_REPORT'}'`

CPU_USER=`echo $CPU_REPORT | awk '{ print $1 }' | cut -d "." -f 1`
CPU_SYSTEM=`echo $CPU_REPORT | awk '{ print $2 }' | cut -d "." -f 1`
CPU_IOWAIT=`echo $CPU_REPORT | awk '{ print $3 }' | cut -d "." -f 1`
CPU_IDLE=`echo $CPU_REPORT | awk '{ print $4 }' | cut -d "." -f 1`
  INTERVAL_SEC는 조사 시간 간격이고 NUM_REPORT는 조사 횟수입니다. 즉, 디폴트 값으로 1초당 1번씩 총 3번 사용률을 구한 후 평균값을 구하는 겁니다. 결과로 얻을 수 있는 정보는 사용자, 시스템, 입출력에 의한 CPU 사용률과 IDLE입니다. 사실 훨씬 간단하게 구할 수도 있지만 다수의 결과에서 평균값을 구하는 것이 더 정확한 것으로 보여 이렇게 작성했습니다.

  참고로 밑에 cut을 이용해 소수점 이하의 값을 잘라버린 이유는 나중에 스크립트 내에서 if 문을 통해 비교를 할 경우 정수가 필요하기 때문입니다. 따라서 그냥 출력만 하려고 할 경우 해당 부분은 없어도 됩니다. 하지만 그럴 경우 '1.33333' 같이 나눠 떨어지지 않는 값이 나올 수도 있으므로 결국 그에 대한 적절한 처리가 필요할 것입니다.

  만약 리눅스가 아닌 유닉스에서 사용하려면 USER, SYS, WAIT, IDLE이 있는 위치(차례대로 각각 $13, $14, $16, $15)를 해당 OS의 vmstat 출력 결과에 맞게 수정해야 합니다. 또한 vmstat 명령어의 옵션을 약간 수정해야 할 수도 있습니다.

2011/07/15 14:48 2011/07/15 14:48
Posted
Filed under 쉘 스크립트
참조 원문 : 8 More Bash Tips for Working Faster With the Shell

  날이면 날마다 오는 배시 관련 포스팅입니다.

앨리어스 등록
  일반적으로 앨리어스를 등록할 때는 ~./bashrc 파일에 아래와 같은 형식으로 원하는 내용을 집어넣습니다.
alias name='command'
  그냥 이렇게 평범하게 등록하고 쓰는 방법도 있지만 앨리어스 전용 파일을 별도로 만들고 그 파일을 읽는다면 관리가 더 깔끔하겠죠? 이를 위해 .bash_aliases 파일을 만들어 앨리어스들을 넣은 후 ~./bashrc 파일에서 아래와 같이 파일을 읽어들입니다.
if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

히스토리를 탐색할 때 페이지 업/다운 키 사용
  ~/.inputrc라는 파일을 만들고 아래의 두 줄을 집어넣으면 PageUp 키와 PageDown 키로 히스토리를 탐색할 수 있습니다. 파일을 만든 후 다시 로그인을 해야 적용됩니다.
"\e[5~": history-search-backward
"\e[6~": history-search-forward

이맥스 스타일의 단축키들
  화살표까지 손을 옮기기 귀찮을 때 사용하면 편합니다.
  • ^A – go to the start of line
  • ^E – go to the end of line
  • ^H – erase one character to the left
  • ^D – erase one character to the right, it also exits the shell by default if there is no character to delete
  • ^U – erase everything from the cursor to start
  • ^K – erase everything from the cursor to end
  • ^P – bring the previous command in history
  • ^N – bring the next command in history
  • ^C – interrupt character, sends SIGTERM to the current application

매뉴얼 페이지에 색깔 적용
  아래의 내용을 ~./bashrc 파일에 넣습니다.
export LESS_TERMCAP_mb=$'\E[01;31m' # begin blinking
export LESS_TERMCAP_md=$'\E[01;38;5;74m' # begin bold
export LESS_TERMCAP_me=$'\E[0m' # end mode
export LESS_TERMCAP_se=$'\E[0m' # end standout-mode
export LESS_TERMCAP_so=$'\E[38;5;246m' # begin standout-mode - info box
export LESS_TERMCAP_ue=$'\E[0m' # end underline
export LESS_TERMCAP_us=$'\E[04;38;5;146m' # begin underline
  그리고 .bashrc 파일을 다시 읽어들입니다.
. ~/.bashrc


파일 이름의 뒷부분에 원하는 문자열 추가
  만약 'filename'이란 이름의 파일이 있을 때 이 파일을 백업 파일로 만들 때는 'filename.backup' 같은 식으로 이름을 바꾸기 마련입니다. 이때 보통 아래와 같은 방법으로 이름을 바꿉니다.
mv filename filename.backup
  이때 아래의 형식으로 명령을 내리면 같은 효과를 볼 수 있습니다.
mv filename{,.backup}
2010/12/29 13:44 2010/12/29 13:44
Posted
Filed under 프로그램과 명령어/관리와 유지보수
참조 원문 : How to Limit The CPU Usage of Any Process in Linux
cpulimit 홈페이지 : http://cpulimit.sourceforge.net/

  cpulimit은 하나의 프로세서가 점유할 수 있는 최대 CPU 사용량을 제한하는 프로그램입니다. 아주 가끔가다가 이런 프로그램이 필요한 경우들이 있죠. 기본적으로 어느 배포판이든 사용 가능합니다. 우분투에서는 아래의 명렁어로 설치가 가능합니다.
sudo apt-get install cpulimit
  사용법도 정말 간단합니다. 제한하려는 프로세스의 PID를 찾고 최대 허용할 CPU 사용량을 정한 다음에 아래와 같은 명령어로 제한할 수 있습니다.
sudo cpulimit -p PID -l CPU
  PID에는 프로세스의 PID, CPU에는 제한할 백분률을 정수로 쓰면 됩니다.


  그리고 우분투 포럼에서 abcuser라는 사람이 이 프로그램을 활용하여 '프로세스들의 CPU 점유율을 모니터링하다가 특정 CPU 점유율을 넘는 프로세스를 감지하면 해당 프로세스를 제한 조치'하는 알흠다운 스크립트를 만들었다고 합니다. 스크립트에서 위의 cpulimit과 gawk를 사용한다고 하니 gawk도 깔아버립시다.
sudo apt-get install gawk
  스크립트는 여기서 받으시거나 위의 포럼 포스트에서 복사하시기 바랍니다. 스크립트는 총 2개로 프로세스들의 CPU 점유율을 감시하고 제한하는 cpulimit_daemon.sh와 시작과 종료를 제어하기 위해 /etc/init.d/ 디렉토리 안에 넣을 cpulimit 파일들로 구성되어 있습니다. cpulimit_daemon.sh 스크립트 내에는 아래의 4개 변수가 있습니다.
  • CPU_LIMIT : 각 프로그램의 최대 CPU 점유율입니다. 디폴트 값은 20(%)입니다.
  • DAEMON_INTERVAL : 스크립트가 시스템을 체크하는 시간 간격입니다. 디폴트 값은 3(초)입니다.
  • BLACK_PROCESSES_LIST : 이 변수에 있는 프로세스만 CPU 점유율을 제한합니다. 디폴트 값은 비어 있으며 그 경우 CPU_LIMIT에서 설정한 점유율을 넘기는 모든 프로세스에 대해 제한합니다. 여러 프로세스를 지정할 때는 파이프(|)를 구분자로 사용합니다.(예: mysql|firefox|gedit)
  • WHITE_PROCESSES_LIST : 이 변수에 있는 프로세스를 제외한 모든 프로세스의 CPU 점유율을 제한합니다. 위와 마찬가지로 디폴트 값은 비어 있으며 그 경우 위와 마찬가지로 CPU_LIMIT에서 설정한 점유율을 넘기는 모든 프로세스에 대해 제한합니다. 마찬가지로 여러 프로세스를 지정할 때는 파이프(|)를 구분자로 사용합니다.(예: mysql|firefox|gedit)
  참고로 BLACK_PROCESSES_LIST와 WHITE_PROCESSES_LIST를 모두 사용할 수는 없으며 둘 중 하나는 비어있어야 합니다.

  이제 설정이 끝났으면 설치 작업에 들어갑니다. 먼저 cpulimit_daemon.sh 스크립트 파일을 /usr/bin/ 디렉토리에 넣고 실행이 가능하도록 퍼미션을 변경합니다.
sudo mv ~/cpulimit/cpulimit_daemon.sh /usr/bin
sudo chown root:root /usr/bin/cpulimit_daemon.sh
sudo chmod 700 /usr/bin/cpulimit_daemon.sh
  그리고 cpulimit 스크립트 파일을 /etc/init.d/ 디렉토리에 넣고 오너와 퍼미션을 변경한 후 부팅 시 자동으로 실행하도록 만듭니다.
sudo mv ~/cpulimit/cpulimit /etc/init.d/
sudo chown root:root /etc/init.d/cpulimit
sudo chmod 700 /etc/init.d/cpulimit
sudo update-rc.d cpulimit defaults
  이제 아래의 명령어들을 통해 서비스를 제어할 수 있습니다.
sudo service cpulimit status   # cpulimit 데몬의 실행 여부 확인
sudo service cpulimit start    # cpulimit 데몬 실행
sudo service cpulimit stop     # cpulimit 데몬 종료

  마지막으로 지금까지 설치한 스크립트들은 아래의 명령어들을 통해 삭제할 수 있습니다.
sudo service cpulimit stop           # 삭제 전 cpulimit 데몬 중지
sudo update-rc.d -f cpulimit remove  # 심볼릭 링크 삭제
sudo rm /etc/init.d/cpulimit         # 데몬 제어 스크립트 삭제
sudo rm /usr/bin/cpulimit_daemon.sh  # 데몬 스크립트 삭제
  만약 cpulimit 프로그램과 gawk 프로그램까지 삭제하려면 아래의 명령어들을 실행합니다.
sudo apt-get remove cpulimit
sudo apt-get remove gawk
2010/10/30 16:04 2010/10/30 16:04
Posted
Filed under 쉘 스크립트
참조 원문 : Linux: Using Remote Wakeup (Wake on LAN)
관련 글 : [시스템] 리눅스에서 WoL 작동시키기

  뭐, 가치가 있는 포스팅인지 모르겠으나 재미있어 보여서 일단 질러봅니다. 대략 아래와 같은 시나리오입니다.

  "재택 근무자들을 위해 WoL용 리눅스 서버를 만들려고 한다. 재택 근무자들은 이 서버를 통해 회사에 있는 자신의 컴퓨터를 부팅할 수 있다. 재택 근무자들은 리눅스에 대해 모르기 때문에 당신은 Putty 같은 클라이언트 프로그램과 그에 맞는 접속 세션 정보가 담긴 파일을 제공하여 쉽게 서버로 접속할 수 있게 해야 한다. 재택 근무자들이 서버에 접속 후 'wol'이라는 이름의 스크립트를 실행하여 원하는 컴퓨터를 킬 수 있게 할 것이다."

  뭐, 별도로 서버를 하나 세워야 한다는 측면에서 이미 상당히 의미 없어보이는 시나리오지만 어쨌든 아이디어 자체는 재미있어 보입니다. 일단 이 일을 실현할 'wol'이라는 스크립트 파일의 내용은 아래와 같습니다.
#!/bin/bash
clear
wolmenu="wol.menu"
woldata="wol.data"
wolloc="`dirname \"$0\"`/"
if [ ! -f $wolloc$wolmenu ]
then
     echo Cannot find my menu file. My files should be in $wolloc.
     elif [ ! -f $wolloc$woldata ]
     then
          echo Cannot find my data file. My files should be in $wolloc.
     else
          cat $wolloc$wolmenu
          echo;echo Type the number of the PC to awaken or c to cancel and press Enter:
          read n
          case $n in
               c) exit;
               ;;
               C) exit;
               ;;
               *) echo Waking up `grep ^$n $wolloc$wolmenu`.;
               ipsubnet=`grep ^$n $wolloc$woldata|cut -d ' ' -f 3`;
               hwaddress=`grep ^$n $wolloc$woldata|cut -d ' ' -f 2`;
               echo The command running is - wakeonlan -i $ipsubnet $hwaddress;
               wakeonlan -i $ipsubnet $hwaddress;
               ;;
     esac
fi
  작가 스스로도 시인했듯이 스크립트가 상당히 허접합니다. 입력 값 검사 같은 루틴이 전혀 없고 WoL이 성공했는지 핑 같은 것으로 검사하는 루틴도 없습니다. 뭐, 그건 넘어가자고 하니 넘어갑시다.

  스크립트를 살짝쿵 훑어봤다면 wol.menu와 wol.data라는 파일이 별도로 사용된다는 것을 알 수 있습니다. 이름에서도 알 수 있지만 wol.menu 파일은 이 wol이라는 스크립트를 실행했을 때 메뉴로 출력할 일반 텍스트 파일이라는 것을 알 수 있습니다. 대충 내용은 아래와 같이 작성하면 되겠죠.
Number PC Name             HW Address         IP Address
====== =================== ================== ================
1      ACCOUNTING          00:11:22:33:44:50  192.168.1.10
2      FINANCE             00:11:22:33:44:51  192.168.1.11
3      MANAGER             00:11:22:33:44:52  192.168.1.12
  wol.data 파일은 실제 WoL의 대상이 될 머신의 MAC 주소와 해당 서브넷의 브로드캐스트 주소를 담을 파일입니다. 내용은 아래와 같은 형식입니다.
1 00:11:22:33:44:50 192.168.1.255
2 00:11:22:33:44:51 192.168.1.255
3 00:11:22:33:44:52 192.168.1.255
  스크립트 파일의 내용을 봤을 때 대충 어떻게 돌아가는지 바로 파악이 되실겁니다. 출력된 메뉴를 보고 번호를 누르면 wol.data 파일에서 해당 번호를 찾아 대상 서브넷의 대상 MAC 주소를 향해 매직 패킷을 날리는 것이죠. 좀 개조하면 wol.data 파일이 필요 없겠군요. 브로드캐스트 주소 때문에 많이 개조해야 할라나...



  이거 말고도 아래와 같은 시나리오도 있군요.

  "월요일~금요일에 한하여 06:30에 특정 컴퓨터들이 켜지게 만들고 싶다. 출근해서 부팅하기 귀찮거든."

  ...좀 어이없지만 어쨌든 아이디어는 재미있습니다. 켜지게 만들려는 컴퓨터들이 있는 서브넷에 항상 켜 있는 리눅스 컴퓨터가 있다면 cron에 아래와 같은 엔트리를 집어넣어 쉽게 위 시나리오를 해결할 수 있습니다.
30 6 * * 1-5 wakeonlan -i 192.168.1.255 -f /home/user/scripts/autowol.data
  물론 IP는 적절히 자신의 서브넷에 맞는 브로드캐스트 주소로 적절히 고쳐야겠죠? 안타깝지만 여기서도 별도의 파일을 사용해야 합니다. 위에서 autowol.data라는 파일을 사용하고 있는데 WoL의 대상 MAC 주소를 담고 있는 파일로 내용은 아래와 같습니다.
00:11:22:33:44:50
00:11:22:33:44:51
00:11:22:33:44:52
00:11:22:33:44:53
00:11:22:33:44:54

  지금까지 쓸데없어 보이는 WoL 스크립트 예제에 대해 살펴봤습니다. 근데 또 모르죠. 설마 이런 스크립트가 필요한 날이 언젠가 올지도...

2010/10/29 20:08 2010/10/29 20:08
Posted
Filed under 쉘 스크립트
참조 원문 : Single vs. Double Quotes in Bash

  배시 쉘을 쓰다보면 백 쿼트(`), 싱글 쿼트('), 더블 쿼트(")의 기능이 햇갈리기 마련입니다. 특히 싱글 쿼트와 더블 쿼트는 똑같이 문자열을 처리하기 위해 존재하지만 둘 사이에는 작은 차이가 있고 그 작은 차이도 파고들면 복잡해집니다.

  백 쿼트(`)는 그 안에 있는 명령어의 실행 결과(정확히는 표준 출력-stdout-)를 다시 입력으로 바꾸는 기능을 가지고 있습니다. 주로 변수에 어떤 명령어의 출력 결과를 집어넣을 때 사용합니다. 말로 설명하면 어렵지만 아래의 예로 쉽게 이해할 수 있으리라 생각합니다.
mirashi@myservlab:~$ echo Today is `date +%A`
Today is Tuesday

mirashi@myservlab:~$ uname
Linux
mirashi@myservlab:~$ UNAME_RET=`uname`
mirashi@myservlab:~$ echo $UNAME_RET
Linux

  이제 싱글 쿼트(')에 대해 알아보겠습니다. 두 싱글 쿼트 사이에 있는 문자열은 어떤 해석도 거치지 않고 문자 그대로 인식합니다. 아래의 예를 봅시다.
mirashi@myservlab:~$ name="Juliet Kemp"
mirashi@myservlab:~$ echo "$name"
Juliet Kemp
mirashi@myservlab:~$ echo '$name'
$name
  이렇듯 싱글 쿼트는 변환이고 해석이고 뭐고 가차 없이 문자열을 있는 그대로 받아들입니다. 참 솔직한 녀석이 아닐 수 없습니다. 그러므로 문자열에 특수 문자가 많을 때 유용합니다. 물론 같은 이유로 변수를 다루는 부분에서 쓰면 영 좋지 않겠죠?


  더블 쿼트(")는 좀 더 유연한 녀석입니다. 안에 있는 문자열에서 몇 가지 특수 문자를 재해석합니다. 재해석하는 문자로는 공식적으로 달러($), 백쿼트(`), 그리고 이스케이프(\ 로 시작) 문자가 있습니다만...디폴트 상태의 배시 쉘이라면 여기에 느낌표(!)가 추가됩니다. 여기에 대한 이야기는 조금 있다가 하도록 하겠습니다.

  더블 쿼트는 문자열 중간에 공백이 있을 때, 변수와 함께 사용할 때, 명령어의 인수로 사용할 문자열에 공백 문자가 포함되어 하나의 인수가 둘로 인식되는 것을 막을 때 유용합니다. 셋 중 앞의 2개는 바로 위에 있는 싱글 쿼트 예제의 첫 번째 줄과 두 번째 줄에서 차례대로 볼 수 있습니다. 아래는 세 번째 내용을 설명하기 위한 예제입니다.
mirashi@myservlab:~$ mkdir my directory
mirashi@myservlab:~$ ls -l
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 directory
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 my
mirashi@myservlab:~$ mkdir "my directory"
mirashi@myservlab:~$ ls -l
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 directory
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 my
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 my directory
  전자의 경우 my와 directory라는 2개의 디렉토리가 만들어졌지만 후자는 공백 문자가 포함된 my directory라는 디렉토리가 만들어졌습니다. 그렇다면 이번에는 변수와 함께 사용할 때 어떻게 유용한지 볼까요?
mirashi@myservlab:~$ NameToRemove="my directory"
mirashi@myservlab:~$ rmdir "$NameToRemove"
mirashi@myservlab:~$ ls -l
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 directory
drwxr-xr-x 2 mirashi mirashi 4096 2010-10-21 19:38 my
mirashi@myservlab:~$ rmdir $NameToRemove
mirashi@myservlab:~$ ls -l
mirashi@myservlab:~$
  둘의 차이가 보이십니까? 변수를 인수로 사용할 때 변수명을 더블 쿼트로 감싸면 변수의 내용 전체가 하나의 인수로 인식되는 것을 볼 수 있습니다. 변수의 내용을 변수명 대신 그대로 대입해보면 위 내용을 쉽게 이해할 수 있으리라 생각합니다. 또 다른 예로 아래와 같은 경우가 있습니다.
mirashi@myservlab:~$ ls
abcdef
mirashi@myservlab:~$ dpkg-query -l abc*
No packages found matching abcdef.
mirashi@myservlab:~$ dpkg-query -l "abc*"
No packages found matching abc*.
  사용자가 있는 디렉토리에는 abcdef라는 이름의 파일이 있습니다. 이 상태에서 abc라는 이름으로 시작하는 패키지를 검색하기 위해 dpkg-query -l abc* 라고 명령어를 실행했습니다. 그랬더니 abc*가 확장되어 abcdef로 변한 다음 dpkg-query 명령어가 실행되는 것을 볼 수 있습니다. 그래서 abc*를 더블 쿼트로 묶어 실행했더니 의도대로 명령어가 실행되는 것을 볼 수 있습니다.


  아까 더블 쿼트에서 달러($), 백쿼트(`), 그리고 이스케이프 문자 외에 느낌표(!)도 재해석되는 문자에 포함된다고 했는데 사실 엄밀히 따지자면 더블 쿼트와는 관계가 없습니다. 배시 쉘에서 느낌표는 '히스토리 익스팬션(History Expansion)'이라는 것의 디폴트 문자로 쓰이는데 이것은 배시 쉘의 히스토리 기능을 이용한 일종의 명령어 단축 실행 기능이라고 할 수 있습니다. 대충 아래와 같은 기능입니다.
mirashi@myservlab:~$ echo "history expansion"
history expansion
mirashi@myservlab:~$ !e
echo "history expansion"
history expansion
  문제는 이 히스토리 익스팬션 문자라는 것은 백슬래시(\)와 싱글 쿼트(')로만 문자 자체로서 표시(Quote)할 수 있다는 겁니다. 그럼 혹자는 '느낌표 앞에 백슬래시를 쓰면 되겠네?'라고 생각하겠지만 아래처럼 그 방법은 안 통합니다.
mirashi@myservlab:~$ echo "hi\!"
hi\!
  느낌표가 히스토리 익스팬션 문자로 쓰이는 것은 막았으나 더블 쿼트에서 재해석되는 문자는 달러($), 백쿼트(`), 이스케이프 문자 밖에 없기 때문에 백슬래시도 그대로 출력되어 버립니다. 그러면 느낌표를 어떻게 해야 출력할 수 있을까요? 혹자는 아래와 같은 해결 방안을 제시할 수도 있습니다.
mirashi@myservlab:~$ echo hi!
hi!
  네. 되긴 됩니다. 근데 쿼트를 아예 안 쓰고 억지고 해결했다는 문제는 제끼더라도 저게 과연 제대로 작동하고 있는 걸까요? 아래를 봅시다.
mirashi@myservlab:~$ echo hi!yo
-bash: !yo: event not found
  결국 눈가리고 아웅이라는 겁니다. 출력은 됐을지언정 그 출력된 느낌표는 여전히 히스토리 익스팬션 문자로서 역할을 하고 있는 것이죠. 이 문제를 해결하는 방법은 2가지가 있는데 하나는 싱글 쿼트를 사용하는 겁니다. 이 포스트에서 가장 먼저 설명했듯이 싱글 쿼트는 다음 싱글 쿼트가 나오기 전까지의 모든 문자를 문자 그대로 받아들입니다. 조금 전에도 히스토리 익스팬션 문자는 백슬래시(\)와 싱글 쿼트(')로만 문자 자체로서 표시할 수 있다고 했었지요.
mirashi@myservlab:~$ echo 'hi!'
hi!
  다른 방법은 히스토리 익스팬션 문자를 다른 것으로 바꾸거나 아예 없애버리는 겁니다. 히스토리 익스팬션 문자는 'histchars'이라는 이름의 쉘 변수를 통해 지정할 수 있습니다. 참고로 오타 아니고 정말 소문자 맞습니다. 아래처럼 저 변수에 빈 값을 넣으면 자연스럽게 이 기능을 사용하지 않게 되는 것이죠.
mirashi@myservlab:~$ histchars=
mirashi@myservlab:~$ echo "hi!"
hi!
  그런데 배시 쉘은 디폴트로 느낌표를 히스토리 익스팬션 문자로 사용하기 때문에 이 방법은 별로 좋은 방법이라고 할 수 없겠습니다. 자기 호스트에서 저렇게 설정하고 저걸 기준으로 스크립트를 짰다가 다른 호스트에 복사해서 그대로 사용했다간...별로 좋지 못하겠죠. 히스토리 익스팬션은 정말 뻘스러운 기능이 아닌가 싶습니다. 마지막으로 원문의 리플에 달린 뻘스럽지만 그래서 좋은 예제를 보며 포스트를 마치겠습니다.
bash$ cost=2.79
bash$ echo "Alice's sister said, \"Gas costs \$$cost/gal"'!'"\""
Alice's sister said, "Gas costs $2.79/gal!"
bash$ echo 'Bob'"'"'s reply was, "$'"$cost"' is too much!"'
Bob's reply was, "$2.79 is too much!"


2010/10/21 18:00 2010/10/21 18:00