[bash] 명령행 옵션 처리하기

Posted 2007/05/18 02:50
getopts optstring name [args]
  • args가 생략되면 기본적으로 위치매개변수(positional parameters)를 해석한다.
  • optstring은 옵션 문자들(-o 형식에서 o)로 구성된(나열된) 문자열이다.
  • 옵션 문자 뒤에 colon(:)이 붙으면 그 옵션은 option's argument를 갖는 것으로 해석한다.
  • 파싱한 옵션 문자를 name 변수에 저장하고, 다음 해석을 위해 OPTIND 변수(초기값 1)를 증가시킨다. 즉, OPTIND는 다음에 해석할 매개변수의 인덱스를 의미한다.
  • option's argument를 가지는 옵션의 경우에, 그 값을 OPTARG 변수에 저장한다.
  • 옵션 해석이 모두 끝나고 옵션이 아닌 매개변수를 만나면 greater-than-zero(false) 값을 리턴한다. 이 때 OPTIND 변수는 옵션이 아닌 첫번째 매개변수를 가리키는 값이 될 것이고, name 변수는 ?로 설정된다.
  • 잘못된 옵션을 만나면 name 변수의 값은 ?로 설정하며, 에러메시지를 출력하고 OPTARG 변수를 unset한다. 한편, OPTERR 변수 값을 0으로 설정하거나, optstring 문자열의 첫번째 문자를 colon(:)으로 하면 에러메시지 출력을 막을 수 있다. 이를 silent  error reporting이라 하며, 이 경우 잘못된 옵션 문자가 OPTARG 변수에 저장된다.

<예제 소스>
#!/bin/bash

usage="usage: $0 [-a] [-b optarg] [-c] args..."

while getopts ":ab:c" opt ; do
    case $opt in
        a ) echo "process -a option" ;;
        b ) echo "process -b option with optarg=$OPTARG" ;;
        c ) echo "process -c option" ;;
        ? ) echo $usage; exit 1
    esac
done

echo "total arguments = $*"

shift $(( $OPTIND - 1 ))

echo "real arguments = $*"

<실행 결과>
gentie bash $ ./optsdemo -a -b barg -c realarg
process -a option
process -b option with optarg=barg
process -c option
total arguments = -a -b barg -c realarg
real arguments = realarg

[bash] case/select statements

Posted 2007/04/29 12:18
case word in
pattern [| pattern] ... )
statements... ;;
...
* )
statements... ;;
esac
  • patternword와 매치될 때에(만) 해당 statements를 실행한다.
  • 파이프문자(|)로 연결하여 pattern을 여러개 사용할 수 있으며, 그 중에 하나와 일치하면(OR와 동일한 의미) statements는 실행된다.
  • C언어의 switch 구문으로 표현하자면 * )default:의 의미이고, ;;break;의 의미이다.

select name in words
do
statements that can use $name, $REPLY...
done
  • 반복문의 하나로 for 구문과 같은 형태이고, in words가 생략되면 기본값은 in "$@"이다.
  • words에 있는 각 항목(name)으로 구성된 선택메뉴를 표준에러(stderr)로 출력한다.
  • 각 항목 앞에는 순서대로 선택번호(1 ~ 항목의 총개수)가 붙고, 마지막 라인으로 PS3 환경변수의 내용(기본값, #?)을 출력한 후 표준입력(stdin)으로 부터 사용자 입력(선택)을 기다린다.
  • 사용자가 번호를 입력하면 해당 항목의 값은 변수 name에 저장되고, 입력내용(번호)은 내장변수 REPLY에 저장된다.
  • 그런 후 $name$REPLY를 사용할 statements를 실행한다.
  • 위 과정이 반복해서 수행되며, 끝내기 위해서는 스크립트 내에서 break 명령(builtin)을 사용하거나 사용자 입력으로 EOF(Ctrl-D)를 입력한다.

[bash] Looping Constructs

Posted 2007/04/26 13:11
for name in words
do
statements that can use $name...
done
  • words(리스트)에 담겨있는 모든 아이템(name)에 대하여 한번씩, statements를 반복한다.
  • in words가 생략된 경우 in "$@"가 기본값이 된다.
  • 종료코드(return status)는 제일 마지막에 실행된 명령의 종료코드를 반환한다.
  • words에 아이템이 없을 경우에 종료코드(return status)는 0(zero)이다.
for (( expr1; expr2; expr3 ))
do
statements...
done
  • First, the arithmetic expression expr1 is evaluated.
  • The arithmetic expression expr2 is then evaluated repeatedly until it evaluates to 0(zero).
  • Each time expr2 evaluates to a non-zero value, statements are executed and the arithmetic expression expr3 is evaluated.
  • If any expression is omitted, it behaves as if it evaluates to 1.
  • The return value is the exit status of the last command in statements that is executed, or false(non-zero) if any of the expressions is invalid.

while conditionals
do
statements...
done
  • conditionals의 종료코드가 true(0)를 반환하는 동안 statements를 반복한다.
  • return status는 statements중에 제일 마지막에 실행된 명령의 종료코드를 반환하며, 아무것도 실행되지 않았을 경우에는 0(zero)를 반환한다.

until conditionals
do
statements...
done
  • while 구문과 반대의 의미로, conditionals의 종료코드가 false(non-zero)를 반환하는 동안 즉, true(0)가 될 때까지 statements를 반복한다.
  • while 구문과 마찬가지로, return status는 statements중에 제일 마지막에 실행된 명령의 종료코드를 반환하며, 아무것도 실행되지 않았을 경우에는 0(zero)를 반환한다.

다음은 PATH 변수에 들어있는 디렉토리를 라인당 하나씩 출력하는 예제이다.
#!/bin/bash

echo "<----------for------------>"
path=${PATH//:/$IFS}
for apath in $path; do
    ls -d $apath
done

echo "<----------while---------->"
path=$PATH:
while [ ! -z $path ]; do
    ls -d ${path%%:*}
    path=${path#*:}
done

echo "<----------until---------->"
path=$PATH:
until [ -z $path ]; do
    ls -d ${path%%:*}
    path=${path#*:}
done

echo "<----------end------------>"

break and continue builtins
  • 루프실행 중간에 빠져(break) 나가거나, 다음 실행으로 계속(continue)할 수 있다.
  • break n or continue n과 같이 중첩된 루프 중 하나를 지정할 수 있다.
  • 0(true)를 반환한다. 단, n이 1보다 크거나 같은 정수가 아닐 경우에는 non-zero(false)를 반환한다.

[bash] if/else statements, 산술연산

Posted 2007/04/19 21:54
if conditionals or list-of-commands
then
    statements...
elif conditionals or list-of-commands
then
    statements...
else
    statements...
fi

Conditional expression
  1. [ condition ]
    test condition
    ▶ [ condition ] and test condition are equivalent.
    ▶ The test builtin command returns 0(true) or 1(false), depending on the evaluation of an expression, condition.
    ▶ You can examine the return value by displaying $?.
    ▶ You can use the return value with && and ||.
    ▶ You can test it using the various conditional constructs.
    ! condition은 논리부정이다.
    ▶ -a(AND), -o(OR)는 논리연산자로 각각 &&, ||의 의미이며 [ condition ] 구문 안에서 사용한다.
    bash $ test 3 -gt 4 && echo true || echo false
    false
    bash $ [ "abc" != "def" -a "abc" == "abc" ]; echo $?
    0
    bash $ test -d $HOME; echo $?
    0
    <숫자값 비교 연산자>
    -eqequal-nenot equal
    -ltless than-leless than or equal
    -gtgreater than-gegreater than or equal
    <문자열 비교 연산자>
    str1 = str2
    str1str2일치하면 참(true)이다.
    str1 != str2
    str1str2일치하지 않으면 참이다.
    str1 < str2
    str1str2보다 작으면 참이다.
    str1 > str2
    str1str2보다 크면 참이다.
    -n strstr널이 아니면(not null) 참이다.
    -z strstr널이면 참이다.
    <파일 검사 연산자>
    -d filedirectory?
    -e fileexists? (also -a)
    -f fileregular file?
    -h filesymbolic link? (also -L)
    -p filenamed pipe?
    -r filereadable?
    -s filenot empty?
    -S fileSocket?
    -w filewritable?
    -x fileexecutable?
    -N filehas been modified since last being read?
    -O fileOwner?
    -G fileGroup?
    file1 -nt file2
    Test if file1 is newer than file2.
    file1 -ot file2
    Test if file1 is older than file2.
    file1 -ef file2
    Test if file1 is a hard link to file2.
    [ condition ] 구문은 다음의 두가지 제한사항이 있다.
    ▷ *나 # 그리고 괄호나 부등호 등의 특수문자를 escape시켜야 한다.
    ▷ 문자열(string)과 수(arithmetic)의 비교연산자가 서로 다르다.
    다행히, bash는 수식과 문자열 연산에 사용되며 escape가 필요없고 비교 연산자를 통일해 사용할 수 있는 (( expr ))과 [[ expr ]] 구문을 제공한다.
  2. (( expr ))
    let "expr"
    ▶ (( expr )) compound command evaluates an arithmetic expression(산술연산식), expr.
    ▶ If the value of the expr is non-zero, the return(exit) status is 0(true); otherwise the return status is 1(false).
    ▶ [ condition ] 구문과 달리 expr을 작성할 때 연산자 escape가 필요없으며, 일반적인 arithmetic(++, --, **, *, /, %, +, -, <=, >=, <, >, ==, !=), logical(!, &&, ||) 그리고 bitwise(~, <<, >>, &, ^, |) 연산을 수행한다.
    ▶ 수식(expr) 안에서의 비교연산자는 C언어 처럼 참이면 1, 거짓이면 0을 반환한다. 이는 배시의 일반적인 참(0)/거짓(1)과는 반대라는 것에 주의한다.
    ▶ $(( expr )), arithmetic expansion, 형식으로 return status(0 or 1)가 아닌 수식을 계산한 결과값(The value of expr)을 참조할 수 있고, 수식의 하나 이상일 경우 맨 마지막 수식의 값을 반환한다.
    ▶ (( expr )) is exactly equivalent to let "expr".
    ▶ The let builtin-command can also execute one or more arithmetic expression. 일반적으로 아래와 같은 형식으로 변수에 그 숫자값을 대입(assign)하기 위해 사용한다. 대입 문자(=) 좌우로 공백이 허용되지 않음에 주의한다.
    let numvar=arithmetic expression
    bash $ let x=2 y=2**3 z=y*3; echo $? $x $y $z
    0 2 8 24
    bash $ ((a=1<<4, b=17&3, c=1/3)); echo $? $a $b $c
    1 16 1 0

    bash $ ((w=(y/x) + ((~ ++x) & 0x0f))); echo $? $x $y $w
    0 3 8 16
    bash $ echo $(( w=(y/x) + ( (~ ++x) & 0x0f ) ))
    13

    bash $ [ \( 3 -gt 2 \) -a \( 4 -le 1 \) ]; echo $?
    1
    bash $ [ $(( (3>2) && (4<=1) )) = 1 ]; echo $?
    1
    bash $ [ $(( (3>2) && (4<=1) )) ]; echo $?
    0
    bash $ (( (3>2) && (4<=1) )); echo $?
    1
    bash $ (( (3>2) || (4<=1) )); echo $?
    0
  3. [[ expr ]]
    ▶ Return a status of 0(true) or 1(false) depending on the evaluation of the conditional expresseion, expr.
    expr은 [ condition ] 구문에서 언급된 Conditional Expressions(숫자값 비교, 문자열 비교, 파일 검사 연산자)와 괄호(grouping), 논리연산자(!, &&, ||)를 사용하여 작성되며 연산자 escape가 필요없다.
    ▶ 기호를(>, <, =, !=)를 이용해 숫자를 비교할 때 문자열로(as strings) 비교된다는 것에 주의한다.
    ▶ 문자열에 대해 == 또는 != 연산자가 사용될 때는 패턴매칭을 수행한다.
    Word splitting and filename expansion are not performed on the words between the [[ expr ]]; tilde(~) expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and quote removal are performed. (see Shell Expansions)
    bash $ [[ (-d $HOME) && (-w $HOME) ]] &&
    > echo "$HOME is a writable directory"
    /home/gentie is a writable directory

    bash $ [[ "abc def .d,x--" == a[abc]*\ ?d* ]]; echo $?
    0
    [[ "abc def d,x" == a[abc]*\ ?d* ]]; echo $?
    1

    bash $ [[  20 -gt 100 ]]; echo $?
    1
    bash $ [[  20 > 100 ]]; echo $?
    0
    bash $ (( 20 > 100 )); echo $?
    1

List of commands
하나 이상의 명령(또는 파이프라인)들로 구성된 리스트이다. ;, &, && 또는 || 제어 연산자(control operators)로 연결되어 구성되며, ;, & 또는 newline으로 마무리한다. 단, 마무리 연산자는 선택사항(optionally)이다.
제어 연산자의 우선순위에서 &&, ||가 ;, &보다 우선이다.
  • &
    셸은 &로 끝나는 명령을 새로운 서브셸에서 비동기적(asynchronously) 즉, 백그라운드로 실행한다.(참조 Job Control) 명령은 즉시 리턴되며 종료코드는 항상 0(true)이고, 명령에 대한 표준입력은 /dev/null로 설정된다.
  • ;
    셸은 ;로 끝나는 명령을 순차적(sequentially)으로 실행하며 명령이 종료될 때 까지 기다린다.
  • &&
    AND lists, 다음과 같은 형식으로 구성된다.
    command1 && command2
    셸은 command1을 실행하고, 그 종료코드가 0(true)일 때 command2를 실행한다.
  • ||
    OR lists, 다음의 형식으로 구성된다.
    command1 || command2
    셸은 command1 을 실행하고, 그 종료코드가 non-zero(false)일 때 command2를 실행한다.
▶ 명령 리스트의 종료코드는 제일 마지막에 실행된 명령의 종료코드를 반환한다.


종료코드의 조합과 조건검사
[ condition ] 등의 조건검사 구문들 또한 어느 shell command처럼 논리연산자인 &&와 ||를 사용하여 서로 조합해 사용할 수 있다. 이는 조건검사 구문들 또한 (builtin) command로 구현되며 결국은 0(true) 또는 non-zero(false) 값의 종료코드를 반환하는 명령으로 분류할 수 있기 때문이다.
if command && command; then
...
if [ condition ] && [ condition ]; then
...
if command && [ condition ]; then
...

  • inverts the bits, a 0-bit becomes 1-bit and vice versa.
  • in all cases ~x equals (-x)-1
byte b0 = 7; // binary: 0000 0111
byte b1 = ~b0; // binary: 1111 1000 ( -8 )

~7 = -7 -1 = -8
~3578 = -3578-1 = -3579
~-1234 = -(-1234)-1 = 1233

Expression이란? spec tutorial
변수(variables spec, tutorial), 연산자(operators) 및 메소드 호출(method calls)로 구성되며, 하나의(only one) 값(primitive value or reference value or void, lvalue in C)으로 평가(evaluating)되는 수식을 일컫는다.

수식 평가(evaluating expression)가 하는 일들
  • 변수에 값을 대입(assignment)한다.
  • 메소드 arguments 혹은 보다 큰 수식의 operand를 위해 값을 계산한다.
  • 실행 흐름을 컨트롤하는데 이용된다. (boolean expression)
// assigns the value 36
int i = 36;

// assigns the value 'K'
char firstInitial = 'K';

// one expression computes and returns a value,
// the other assigns that value to x
x = 1 + 1;

// concatenates two Strings and returns
// the resulting String
System.out.print("My Name: " + person.getName());

// compares two integers and returns a boolean
while(count < 100) {
...
}

Statements(구문, 절) spec tutorial
The sequence of execution of a program is controlled by statements, which are executed for their effect and do not have values(no lvalue).
  1. expression statements
    Expression statements are terminated by the semicolon character ';'.
    • Assignment (= op=)
    • Any use of ++ or --
    • Object creation expressions
    • Method call
    int age = 19;
    age++;
    myObject.incrementCount();
    String city = new String("London");
    city = city.trim();
  2. declaration statements
    double aValue = 8933.234; // declaration statement
  3. control flow statements

Blocks(블럭) spec
  • A block is a group of zero or more statements between balanced braces and can be used anywhere a single statement is allowed.
  • A block is a sequence of statements wrapped in curly braces '{}'.
  • Any variables declared within the block are local to that block. Local variables expire upon exiting the block in which they are declared.
  • Also, variables declared within a block shadow (or hide) instance variables with the same name.

유닉스 명령(스크립트, 실행파일)의 표준출력변수값처럼 사용할 수 있다.

$(unix_command) or `unix_command`

echo $(ls $(pwd))
현재 디렉토리의 파일 목록을 출력한다.

vi $(grep -l 'search string' *)
'search string' 문자열을 포함한 파일을 모두 vi로 연다.

mail $(who | cut -d' ' -f1)
현재 로그인 상태인 모든 사용자들에게 메일을 보낸다.

문자열 연산으로 변수값에 대해 할 수 있는 것들
  • 변수의 존재와 널(null) 여부를 검사한다.
  • 변수에 기본값을 설정한다.
  • 변수를 설정하지 않아서 생기는 오류를 피할 수 있다.
  • 패턴에 의거 변수값의 일부분을 잘라내거나 바꾼다.

문법 및 예제

${variable:-word}
variable이 존재하지 않거나 널(null)이면 word를 돌려준다. 아니면 variable에 지정된 값($variable)을 돌려줄 것이다.

${variable:=word}
variable이 존재하지 않거나 널(null)이면 variable의 (기본)값을 word로 설정한 후, 그 값을 돌려준다. ${variable:-word}와 비교해서 변수에 값을 설정하느냐 않느냐의 차이가 있다. 따라서 위치 매개변수 같은 read-only 변수에는 적용할 수 없다.

${variable:+word}
${variable:-word}와는 반대의 의미이다. 즉, variable이 존재하지 않거나 null이면 그대로 null을 돌려주고, variable이 이미 정의되어 있으면 word를 돌려준다. 즉, 이미 정의되어 존재하는 변수값을 새로운 값으로 대체하는 역할을 한다. 예를들어, ${count:+1}은 count가 이미 정의되어 있을 때 1을 돌려주고 아니면 null을 돌려준다.

${variable:?message}
variable이 존재하지 않거나 null이면 variable:message를 출력하고 스크립트를 종료한다.

${variable:offset}
${variable:offset:length}
$variable 문자열에서 offset에서 시작해 length(길이) 만큼 뽑아낸 substring을 돌려준다. length가 생략되면 문자열의 끝까지 뽑아낸다. 예를들어, str 변수의 값이 frogfootman으로 설정되었을 때 ${str:4}는 footman을, ${str:4:4}는 foot를 돌려줄 것이다.
variable이 @인 경우, length는 offset 위치의 매개변수에서 시작하는 위치매개변수의 개수이다.

${variable#pattern}
${variable##pattern}
$variable 문자열 값의 맨 앞에서 부터 검사해서 pattern과 일치하는 가장 짧은 부분(#) 혹은 가장 긴 부분(##)삭제하고 나머지 부분을 리턴한다.
${variable%pattern}
${variable%%pattern}
$variable 문자열 값의 맨 뒤에서 부터 검사해서 pattern과 일치하는 가장 짧은 부분(%) 혹은 가장 긴 부분(%%)삭제하고 나머지 부분을 리턴한다.
변수 path의 값이 "/home/my/dir/long.file.name"으로 설정되어 있다고 가정하면,

${path#/*/}my/dir/long.file.name
${path##/*/}long.file.name
${path%.*} /home/my/dir/long.file
${path%%.*}/home/my/dir/long
basename=${path##*/} long.file.name
dirname=${path%/*}/home/my/dir

${variable/pattern/string}
${variable//pattern/string}
pattern과 일치하는 부분을 string으로 바꾼다. 처음 일치하는 부분만 한번(/) 바꾸거나, 일치하는 부분을 모두(//) 바꾼다. pattern#로 시작하면 앞에서 부터, %로 시작하면 뒤에서 부터 검사한다. 예를들어, echo -e ${PATH//:/"\n"} 명령은 PATH 환경변수에 있는 디렉토리들을 라인당 하나씩 출력한다.

${#variable}
$variable 값의 문자열 길이를 리턴한다. 예를들어, 변수 filename의 값이 alice.c일 때 ${#filename}은 7을 리턴한다.

[bash] 매개변수 및 지역변수

Posted 2007/04/13 20:14
이름(name) 우선순위
  1. alias
  2. function, if, for... 등의 키워드
  3. function name
  4. cd, type... 등의 builtin command
  5. PATH 환경변수 내의 디렉토리에 존재하는 script, 프로그램
$ type -all ls
ls is aliased to `ls --color=auto'
ls is /bin/ls

위치 매개변수 (positional parameter)
  $N
N은 매개변수의 위치(순서), $0은 스크립트명, $1은 첫번째, $2는 두번째... 매개변수를 의미한다.
  $*
모든 매개변수로 구성된 하나의 문자열, IFS(Internal Field Seperator)의 첫번째 문자(기본적으로 스페이스)로 구분된다.
  $@
큰따옴표로 묶은 N개의 독립된 문자열(공백으로 구분) 즉, "$1" "$2" ... "$N"
  $#
매개변수의 총 개수

함수의 매개변수와 지역변수
함수 내에서의 $N은 스크립트가 아닌 함수에 대한 매개변수를 값으로 갖지만 $0은 여전히 스크립트명을 값으로 갖는다. 또한 함수 내에서 선언된 변수는 local로 선언하지 않으면 기본적으로 전역적(global)이다.

${varname}은 왜 존재하는가?
10번째 매개변수를 참조하고자 할 때 $10은 틀린 표현이다. ${10}를 사용해야 할 것이다.
다른 상황으로, 가령 $UID와 $USER를 밑줄로 연결하고자 할 때는?
$ echo $UID_$USER
myid
$ echo ${UID}_${USER}
1000_myid


[bash] 기초

Posted 2007/04/12 10:02
중괄호 확장
머리말(preamble), 본문(중괄호로 묶어 콤마로 분리된), 꼬리말(postscript)
$ echo b{ed,olt,ar}s
beds bolts bars

입출력 재지정 (redirection)

cat 명령의 표준입력을 file1으로 재지정하고, 표준출력을 file2로 재지정하는 예, cp file1 file2와 같은 의미이다.
$ cat < file1 > file2

파이프라인 (pipeline)
명령의 출력을 다른 명령의 입력으로 연결한다. 다음은 cut명령을 이용해 /etc/passwd의 각 라인의 내용을 ":"으로 구분해(-d:) 첫필드(-f1)를 가져와 정렬하는 예제이다.
$ cut -d: -f1 < /etc/passwd | sort

인용부호 (quoting)
다음 두 명령줄의 실행결과를 예측해 보자.
$ echo 2 * 3 > 5 is a valid inequality.
$ echo '2 * 3 > 5' is a valid inequality.

명령행 편집
CTRL-B한 문자 왼쪽으로 커서 이동
CTRL-F한 문자 오른쪽으로 커서 이동
DEL, CTRL-D오른쪽 한 문자 삭제
CTRL-K오른쪽에서 명령행 끝까지 삭제
CTRL-Y마지막으로 삭제한 워드를 되살림
CTRL-A명령행 맨 처음으로 커서 옮김
CTRL-E명령행 맨 마지막으로 커서 옮김
CTRL-P이전 명령 히스토리, 위쪽 화살표 키와 같음
CTRL-N다음 명령 히스토리, 아래쪽 화살표 키와 같음
CTRL-R명령 히스토리 검색
CTRL-MRETURN과 동일
CTRL-L화면을 지우고 커서를 화면 가장 위로 이동