• 셸 스크립트 작성하기
    Linux 2024. 11. 19. 00:37
    반응형

       Shebang 지정

    상단에 shebang(#!, 스크립트를 실행할 셸/인터프리터)을 지정해줘야 한다.

    지정하지 않았을 경우 기본 셸로 실행된다.

     

    기본 셸은 다음 명령어로 확인할 수 있다.

    echo $SHELL

     

    bash일 경우

    #!/bin/bash

     

    zsh일 경우

    #!/bin/zsh

    zsh에서만 지원되는 문법을 사용할 경우 상단에 zsh 명시를 해줘야 제대로 실행된다.

     

       주석 처리

    #을 붙여 나타낸다.

    e.g.

    # Installation script for ros2

     

       변수

    변수 선언

    일반적으로 일반 변수는 snake_case로, 환경 변수는 SCREAMING_SNAKE_CASE로 쓰는 것 같다.

    = 전후로 공백이 없어야 한다.

    e.g.

    workspace_path="/home/user/catkin_ws"

     

    변수 사용

    $를 붙여 나타낼 수 있다.

    중괄호를 사용하여 가독성을 높일 수도 있다.

    workspace_path="/home/user/catkin_ws"
    echo "Path is set: $workspace_path"
    echo "Path is set: ${workspace_path}"
    Path is set: /home/user/catkin_ws
    Path is set: /home/user/catkin_ws

     

       출력(echo)

    e.g.

    echo "Installing ROS2 Humble..."
    Installing ROS2 Humble...

     

       입력(read)

    e.g.

    read workspace_path
    echo "Path is set: $workspace_path"
    /home/user/catkin_ws
    Path is set: /home/user/catkin_ws

     

       조건문

    다음과 같은 형태로 사용 가능하다.

    if [ condition1 ]; then
        command1
    elif [ condition2 ]; then
        command2
    else
        command3
    fi

     

    조건 뒤에 세미콜론을 빼려면 then을 다음 줄로 내려야 한다.

    if [ condition1 ]
    then
        command1
    elif [ condition2 ]
    then
        command2
    else
        command3
    fi

     

    조건이 여러 개일 경우 &&, ||를 사용하여 나타낼 수 있다.

    if [ condition1 ] && [ condition2 ]; then
        command1
    else
        command2
    fi
    
    if [ condition1 ] || [ condition2 ]; then
        command1
    else
        command2
    fi

     

    조건 옵션

    -f 파일이 존재하는지 확인
    -d 디렉토리가 존재하는지 확인
    -e 파일이 존재하는지 확인 (파일, 디렉토리 포함 모든 유형 체크)
    -r 읽기 가능한 파일인지 확인
    -w 쓰기 가능한 파일인지 확인
    -x 실행 가능한 파일인지 확인
    -s 파일 크기가 0보다 큰지 확인 (empty check)

     

    e.g.

    if [ -f "test.txt" ]; then
        echo "Test file exists."
    else
        echo "No test file exists."
    fi

    파일이 존재할 경우

    Test file exists.

    파일이 존재하지 않을 경우

    No test file exists.

     

    숫자 비교 연산자

    -eq 같음(equal)
    -ne 같지 않음(not equal)
    -lt 작음(less than)
    -le 작거나 같음(less or equal)
    -gt 큼(greater than)
    -ge 크거나 같음(greater or equal)

     

    e.g.

    num1=7
    num2=28
    
    if [ $num1 -lt $num2 ]; then
        echo "$num1 is less than $num2."
    else
        echo "$num1 is not less than $num2."
    fi
    7 is less than 28.

     

    문자열 비교 연산자

    = 같음
    != 같지 않음
    -z 비어 있음(zero length, empty())
    -n 비어 있지 않음(non-zero length, !empty())

     

    e.g.

    test_file_path="/home/user/test.txt"
    
    if [ -z "$test_file_path" ]; then
        echo "File path is not set."
    else
        echo "File path is set."
    fi
    File path is set.

     

    빈 문자열일 경우

    test_file_path=""
    
    if [ -z "$test_file_path" ]; then
        echo "File path is not set."
    else
        echo "File path is set."
    fi
    File path is not set.

     

    $test_file_path가 아닌 "$test_file_path"로 쓰는 것이 좋다.

    전자로 써도 되지만 test_file_path 변수가 비어 있거나 공백이 들어있을 경우에는 에러가 발생한다.

    따라서 문자열 비교 시 변수를 큰 따옴표로 감싸주는 것이 좋다.

     

    그리고 bash와 zsh로 실행할 거라면 조건에 [] 대신 [[]]를 사용하는 것이 좋다.

    안전성과 유연성이 강화된 버전으로 공백으로 인한 에러를 방지해주고, 안에 정규식도 사용할 수 있다고 한다.

    단, POSIX 표준이 아니라 모든 셸에서 사용할 수는 없는 듯.

    (참고: https://stackoverflow.com/questions/669452/are-double-square-brackets-preferable-over-single-square-brackets-in-b)

     

    정상 실행

    test_file_path=""
    
    if [[ "$test_file_path" = "/home/user/test.txt" ]]; then
        echo "Same string."
    else
        echo "Different string."
    fi
    Different string.

     

    에러 발생

    test_file_path=""
    
    if [ $test_file_path = "/home/user/test.txt" ]; then
        echo "Same string."
    else
        echo "Different string."
    fi
    ./test-script.sh:6: parse error: condition expected: =
    Different string.

     

       반복문

    while문

    다음과 같은 형태로 사용한다.

    while [ condition ]; do
        command
    done

    e.g.

    i=0
    while [ $i -lt 5 ]; do
        echo "num: $i"
        ((i++))
    done
    num: 0
    num: 1
    num: 2
    num: 3
    num: 4

    (()) 안에 C스타일 구문을 넣어 작성할 수 있다.

     

    for문

    다음과 같은 형태로 사용한다.

    for var in list; do
        command
    done

    e.g.

    for i in {0..4}; do
        echo "num: $i"
    done
    num: 0
    num: 1
    num: 2
    num: 3
    num: 4

     

       함수

    함수 선언

    다음과 같은 형태로 사용한다.

    function_name() {
        command
    }

    e.g.

    rosv2() {
        echo "Activate ros2"
    }

     

    함수 호출

    함수 이름을 그냥 쓰면 된다.

    e.g.

    rosv2
    Activate ros2

     

    function 키워드를 명시적으로 써서 가독성을 높일 수도 있는데, 이때는 괄호를 떼고 써도 된다.

    근데 그냥 위처럼 쓰는 게 편한 듯.

    function rosv2 {
        echo "Activate ros2"
    }

     

    인자가 있을 경우

    $1, $2, …와 같은 형태로 여러 개의 인자를 넘겨줄 수 있다.

    e.g.

    package_install() {
        ans=$1
    
        if [ "$ans" = "yes" ]; then
            echo "do installation."
        else
            echo "do not installation."
        fi
    }
    
    package_install "yes"
    do installation.

     

       에러 처리/디버깅

    set 명령어에 여러 옵션을 줘서 에러 처리나 디버깅에 사용할 수 있다.

     

    사용 가능한 옵션

    엄청 많지만 일부만 정리한다.

    -n 스크립트를 실행하지 않고 문법 오류만 체크한다. (noexec)
    -e 에러 발생 시 스크립트 실행을 즉각 중단한다. (errexit)
    -u 선언되지 않은 변수 사용 시 에러를 출력한다. (nounset)
    -x 실행되는 명령어와 각 명령어에 전달되는 인수를 터미널에 출력한다. (xtrace)

     

    -x 추적 시작, +x 추적 종료임을 이용해서 다음과 같이 일부 구간에서만 사용할 수도 있다.

    set -x
    echo "Trace on."
    
    set +x
    echo "Trace off."
    
    set -x
    echo "Trace on again."
    +./test-script.sh:5> echo 'Trace on.'
    Trace on.
    +./test-script.sh:7> set +x
    Trace off.
    +./test-script.sh:11> echo 'Trace on again.'
    Trace on again.

     

       스크립트 실행

    실행 권한 부여

    작성한 스크립트에 실행 권한을 부여해줘야 실행할 수 있다.

    chmod +x test-script.sh

     

    실행 방법

    방법 1) 현재 로그인 된 셸에서 스크립트를 실행할 수 있다.

    ./test-script.sh

     

    방법 2) 셸을 직접 지정해준 후 실행할 수도 있다.

    shebang으로 실행할 셸을 지정하지 않았을 경우 지정해준 후 실행하는 것이 좋다.

     

    bash일 경우

    bash test-script.sh

     

    zsh일 경우

    zsh test-script.sh

     

       스크립트 예시

    공식 문서에 나와 있는 설치 가이드대로 우분투 22.04에 ROS2 Humble Hawksbill desktop를 설치하고, 워크 스페이스를 생성한 뒤 빌드까지 해주는 스크립트를 작성해보자. (Bash 기준)

     

    #!/bin/bash
    
    # Set locale
    current_lang=$(locale | grep LANG= | cut -d= -f2)
    if [[ "$current_lang" != "en_US.UTF-8" ]]; then
        echo "Set UTF-8 locale."
        sudo apt update && sudo apt install -y locales
        sudo locale-gen en_US.UTF-8
        sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
        export LANG=en_US.UTF-8
    else
        echo "Locale is already set."
    fi
    
    # Set ubuntu universe repository
    sudo apt install -y software-properties-common
    sudo add-apt-repository -y universe
    
    # Add ROS2 GPG key
    sudo apt update && sudo apt install curl -y
    sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
    
    # Add the repository in sources list
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
    
    # Update the packages to the latest version
    sudo apt update -y
    sudo apt upgrade -y
    echo "Repository setup complete."
    
    # Install desktop version
    sudo apt install -y ros-humble-desktop
    
    # Install development tools
    sudo apt install -y ros-dev-tools
    
    # Create workspace
    install_path="$HOME/ros2_ws"
    mkdir -p "${install_path}/src"
    cd $install_path
    
    # Source bash file
    source /opt/ros/humble/setup.bash
    echo "Desktop installation complete."
    
    # Install colcon
    sudo apt install -y python3-colcon-common-extensions
    echo "Colcon installation complete."
    
    # Build
    colcon build
    
    echo "All installations are complete."

     

    설치가 끝나면 source 해준 후, 데모 노드를 실행해보고 잘 되는지 확인한다.

    source /opt/ros/humble/setup.bash
    ros2 run demo_nodes_cpp talker
    [INFO] [1732033177.977694275] [talker]: Publishing: 'Hello World: 1'
    [INFO] [1732033178.977679343] [talker]: Publishing: 'Hello World: 2'
    [INFO] [1732033179.977680602] [talker]: Publishing: 'Hello World: 3'
    [INFO] [1732033180.977688773] [talker]: Publishing: 'Hello World: 4'
    [INFO] [1732033181.977681876] [talker]: Publishing: 'Hello World: 5'
    [INFO] [1732033182.977708902] [talker]: Publishing: 'Hello World: 6'
    [INFO] [1732033183.977707750] [talker]: Publishing: 'Hello World: 7'
    [INFO] [1732033184.977713308] [talker]: Publishing: 'Hello World: 8'
    [INFO] [1732033185.977717267] [talker]: Publishing: 'Hello World: 9'
    [INFO] [1732033186.977715467] [talker]: Publishing: 'Hello World: 10'
    [INFO] [1732033187.977729032] [talker]: Publishing: 'Hello World: 11'

     

    잘 된다.

    반응형

    댓글

ABOUT ME

공부한 것을 기록하기 위해 블로그를 개설했습니다.

VISIT

/