명령으로 실행할 때 문자열 변수를 참조할 수 없는 이유는 무엇입니까?

명령으로 실행할 때 문자열 변수를 참조할 수 없는 이유는 무엇입니까?

내 Ubuntu/Debian 기반 Linux 업데이트 POSIX 셸 스크립트에 필요한 것 같습니다.큰따옴표가 아님실행 중인 저장된 명령이 포함된 문자열 변수입니다. 이 질문을 이해하지 못해서 이유를 묻고 싶습니다. 코드가 정확하면 어떻게 되나요?

경고: SC2086,위키피디아, "큰따옴표는 와일드카드와 단어 분리기를 방지합니다."

스크립트는 다음과 같으며 문제가 있는 부분을 강조 표시했습니다.

#!/bin/sh

# exit script when it tries to use undeclared variables
set -u

# color definitions
readonly red=$(tput bold)$(tput setaf 1)
readonly green=$(tput bold)$(tput setaf 2)
readonly yellow=$(tput bold)$(tput setaf 3)
readonly white=$(tput bold)$(tput setaf 7)
readonly color_reset=$(tput sgr0)
# to create blocks of texts, I separate them with this line
readonly block_separator='----------------------------------------'

step_number=0
execute_jobs ()
{
    while [ ${#} -gt 1 ]
    do
        job_description=${1}
        job_command=${2}

        step_number=$(( step_number + 1 ))

        printf '%s\n' "Step #${step_number}: ${green}${job_description}${color_reset}"

        printf '%s\n' "Command: ${yellow}${job_command}${color_reset}"

        printf '%s\n' "${white}${block_separator}${color_reset}"

        # RUN THE ACTUAL COMMAND
        # ShellCheck warns me I should double quote the parameter
        # If I did, it would become a string (I think) and I'd get 'command not found' (proven)
        # As I don't understand the issue, I left it as I wrote it, without quotes
        ### shellcheck disable=SC2086
        if sudo ${job_command} # <-- HERE
        then
            printf '\n'
        else
            printf '%s\n\n' "${red}An error occurred.${color_reset}"
            exit 1
        fi

        shift 2
    done
}

execute_jobs \
    'configure packages'                         'dpkg --configure --pending' \
    'fix broken dependencies'                    'apt-get --assume-yes --fix-broken install' \
    'update cache'                               'apt-get update' \
    'upgrade packages'                           'apt-get --assume-yes upgrade' \
    'upgrade packages with possible removals'    'apt-get --assume-yes dist-upgrade' \
    'remove unused packages'                     'apt-get --assume-yes --purge autoremove' \
    'clean up old packages'                      'apt-get autoclean'

답변1

여기서는 변수를 참조할 수 없습니다.

if sudo ${job_command}

당신 때문에하다단어 분할을 원합니다. 인용하면 명령은 (첫 번째 단계의 경우)가 됩니다.

if sudo "dpkg --configure --pending"

그리고 오류 메시지에서 알 수 있듯이 인수 및 인수가 있는 명령이 sudo아닌 명령 을 찾으십시오 .dpkg --configure --pendingdpkg--configure--pending

sudo: dpkg --configure --pending: command not found

(더 명확하게 하려면 추가 공백을 사용해 보십시오.)

따옴표를 생략하면 쉘이 인수를 분할하고 모든 것이 예상대로 작동합니다.

답변2

지금 코드를 작성하는 방식에서는 셸을 사용하여 명령이 포함된 문자열을 추가 옵션 및 기타 매개변수가 포함된 명령 이름으로 올바르게 분할합니다. 쉘은 따옴표 없이 변수를 확장하는 경우에만 이를 수행합니다.

그러나 쉘은 이를 수행하지 않습니다.바르게명령 문자열에 공백으로 분할해야 하는 매개변수와 분할하면 안 되는 매개변수가 포함되어 있는 경우 또는 확장하면 안 되는 파일 이름 글로빙 패턴처럼 보이는 하위 문자열이 포함되어 있는 경우.

스크립트의 대체 구현을 사용하면 모든 확장명을 올바르게 인용할 수 있습니다.

#!/bin/sh

step=0
execute_job () {
    step=$(( step + 1 ))
    printf 'Step #%s: "%s"\n' "$step" "$1"
    shift
    printf 'Command: %s\n' "$*"

    if sudo -- "$@"; then
        echo Done
    else
        echo 'Some error occured'
        exit 1
    fi
}

execute_job 'configure packages'      dpkg --configure --pending
execute_job 'fix broken dependencies' apt-get --assume-yes --fix-broken install
# etc.

여기서는 execute_job모든 직업이 아닌 개별 직업에만 관심을 두고 있습니다. 함수의 첫 번째 매개변수를 작업 설명 문자열로 처리합니다. 그런 다음 실행할 명령으로 추가 인수를 전달합니다. 실행되어야 하는 명령은 다음과 같이 전달됩니다.일부단일 문자열 대신 매개변수를 사용합니다.

명령 문자열을 인쇄할 때 위치 매개변수와 첫 번째 문자를 구분 기호로 "$*"연결한 작은따옴표 문자열(기본값은 공백)을 사용합니다.$IFS

명령을 실행할 때 "$@"단어 분할과 파일 이름 글로빙을 방지하기 위해 개별적으로 인용되는 위치 인수 목록을 사용합니다.

이렇게 하면 함수를 사용하여 다음과 같은 작업을 수행할 수 있습니다.

execute_job 'printf test' printf '%s\n' 'line 1' 'line 2'

... 두 개의 별도 줄(또는 공백, 탭, 줄 바꿈 또는 와일드카드가 포함된 인수가 있는 다른 명령)에 line 1및 를 인쇄합니다.line 2

관련 정보