Bash에 "콜백"이라는 프로그래밍 개념이 있습니까?

Bash에 "콜백"이라는 프로그래밍 개념이 있습니까?

프로그래밍에 대해 읽으면서 몇 번 "콜백"이라는 개념을 접했습니다.

흥미롭게도 나는 "콜백 함수"라는 용어에 대해 "교훈적"이거나 "명확하다"고 부를 수 있는 설명을 한 번도 찾지 못했습니다(내가 읽은 거의 모든 설명은 다른 설명과 매우 다른 것 같습니다). 혼란스러운).

Bash에 "콜백"이라는 프로그래밍 개념이 있습니까? 그렇다면 작고 ​​간단한 Bash 예제로 답변해 주세요.

답변1

전형적인명령형 프로그래밍에서는 명시적인 제어 흐름을 통해 차례로 실행되는 일련의 명령을 작성합니다. 예를 들어:

if [ -f file1 ]; then   # If file1 exists ...
    cp file1 file2      # ... create file2 as a copy of a file1
fi

등.

예제에서 볼 수 있듯이 명령형 프로그래밍에서는 실행 흐름을 매우 쉽게 따라갈 수 있습니다. 항상 주어진 코드 줄에서 시작하여 실행 컨텍스트를 결정하고 사용자가 제공하는 모든 명령이 구현된다는 것을 알 수 있습니다. 프로세스의 위치(또는 함수를 작성하는 경우 호출 사이트의 위치)입니다.

콜백이 흐름을 변경하는 방법

콜백을 사용할 때 명령 집합의 사용을 "지리적"으로 배치하는 것이 아니라 호출해야 하는 시기를 설명하는 것입니다. 다른 프로그래밍 환경의 일반적인 예로는 "이 리소스를 다운로드하고 다운로드가 완료되면 이 콜백을 호출하세요."와 같은 상황이 있습니다. Bash에는 이러한 일반적인 콜백 구조가 없지만 콜백이 있습니다.오류 처리예를 들어 다른 상황도 있습니다(먼저 이해해야 합니다.명령 대체그리고 슬램종료 모드이 예를 이해하세요):

#!/bin/bash

scripttmp=$(mktemp -d)           # Create a temporary directory (these will usually be created under /tmp or /var/tmp/)

cleanup() {                      # Declare a cleanup function
    rm -rf "${scripttmp}"        # ... which deletes the temporary directory we just created
}

trap cleanup EXIT                # Ask Bash to call cleanup on exit

직접 해보고 싶다면 위의 내용을 파일에 저장하세요. 예를 들어 cleanUpOnExit.sh실행 가능하게 만들고 실행하세요.

chmod 755 cleanUpOnExit.sh
./cleanUpOnExit.sh

여기 내 코드는 cleanup함수를 명시적으로 호출하지 않습니다. Bash에게 trap cleanup EXIT,"Bash님께, cleanup종료할 때 이 명령을 실행해 주세요"( cleanup이것은 제가 이전에 정의한 함수이지만 Bash가 이해하는 모든 것일 수 있습니다). Bash는 치명적이지 않은 모든 신호, 종료, 명령 실패 및 일반 디버깅을 지원합니다(각 명령 전에 실행할 콜백을 지정할 수 있음). 여기서 콜백은 cleanup쉘이 종료되기 전에 Bash에 의해 "콜백"되는 함수입니다.

쉘 인수를 명령으로 평가하는 Bash의 기능을 활용하여 콜백 지향 프레임워크를 구축할 수 있습니다. 이는 이 답변의 범위를 약간 벗어나며 함수 전달에 항상 콜백이 포함된다고 제안하여 더 많은 혼란을 초래할 수 있습니다. 바라보다Bash: 함수를 인수로 전달기본 기능의 몇 가지 예입니다. 이벤트 처리 콜백과 마찬가지로 여기서의 아이디어는 함수가 다른 함수뿐만 아니라 데이터를 인수로 사용할 수 있다는 것입니다. 이를 통해 호출자는 동작과 데이터를 모두 제공할 수 있습니다. 이 접근 방식의 간단한 예는 다음과 같습니다.

#!/bin/bash

doonall() {
    command="$1"
    shift
    for arg; do
        "${command}" "${arg}"
    done
}

backup() {
    mkdir -p ~/backup
    cp "$1" ~/backup
}

doonall backup "$@"

cp( 참고로 여러 파일을 처리할 수 있기 때문에 이것이 약간 쓸모 없다는 것을 알고 있습니다 .)

doonall여기서는 인수로 제공된 다른 명령을 받아들이고 이를 나머지 인수에 적용하는 함수를 만든 다음 이를 사용하여 backup스크립트에 제공된 모든 인수로 함수를 호출합니다. 결과는 모든 매개변수를 하나씩 백업 디렉터리에 복사하는 스크립트입니다.

이 접근 방식을 사용하면 단일 책임으로 함수를 작성할 수 있습니다. doonall책임은 모든 인수에 대해 한 번에 하나씩 작업을 실행하는 것입니다. backup책임은 백업 디렉터리에 해당 (유일한) 인수의 복사본을 만드는 것입니다. doonallbackup다 다른 컨텍스트에서 사용될 수 있으므로 더 많은 코드 재사용, 더 나은 테스트 등이 가능합니다.

이 경우 콜백은 함수 backup이고 doonall다른 모든 매개변수를 "콜백"하도록 지시합니다. 즉, doonall동작(첫 번째 매개변수)과 데이터(나머지 매개변수)를 제공합니다.

(두 번째 예에서 설명한 사용 사례에서는 "콜백"이라는 용어를 직접 사용하지 않지만 이는 내가 사용하는 언어에서 비롯된 습관일 수 있습니다. 함수나 람다를 전달하는 것으로 생각합니다. 이벤트 시스템에 콜백 등록의 맥락이 아닙니다).

답변2

첫째, 기능이 무엇인지가 아니라 사용 방식에 따라 함수가 콜백 함수가 된다는 점에 유의하는 것이 중요합니다. 콜백은 작성하지 않은 코드에서 작성한 코드를 호출하는 것입니다. 특정 이벤트가 발생하면 시스템에 다시 전화하도록 요청합니다.

쉘 프로그래밍의 콜백 예는 트랩입니다. 트랩은 함수가 아니라 평가할 코드 조각으로 표시되는 콜백입니다. 쉘이 특정 신호를 수신하면 쉘에 코드를 호출하도록 요청합니다.

콜백의 또 다른 예는 명령 -exec의 작업 입니다 find. 이 명령이 하는 일은 find디렉터리를 반복적으로 반복하고 각 파일을 차례로 처리하는 것입니다. 기본적으로 처리는 파일 이름을 인쇄하는 것(암시적으로 -print)이지만 -exec처리는 사용자가 지정한 명령을 실행하는 것입니다. 이는 콜백의 정의에 적합하지만 콜백이 별도의 프로세스에서 실행되기 때문에 그다지 유연하지는 않습니다.

find와 같은 함수를 구현하는 경우 콜백 함수를 사용하여 각 파일을 호출하도록 할 수 있습니다. 이는 함수 이름(또는 외부 명령 이름)을 인수로 사용하고 현재 디렉터리와 하위 디렉터리의 모든 일반 파일에 대해 이를 호출하는 매우 단순화된 찾기 같은 함수입니다. 이 함수는 call_on_regular_files일반 파일이 발견될 때마다 호출되는 콜백 역할을 합니다.

shopt -s globstar
call_on_regular_files () {
  declare callback="$1"
  declare file
  for file in **/*; do
    if [[ -f $file ]]; then
      "$callback" "$file"
    fi
  done
}

쉘은 주로 간단한 프로그램을 위해 설계되었기 때문에 쉘 프로그래밍에서는 다른 환경에서처럼 콜백이 일반적이지 않습니다. 콜백은 데이터 및 제어 흐름이 독립적으로 작성된 코드 부분과 배포된 코드 부분(기본 시스템, 다양한 라이브러리, 애플리케이션 코드) 사이를 오가는 경향이 있는 환경에서 더 일반적입니다.

답변3

"콜백"은 단순히 다른 함수에 인수로 전달되는 함수입니다.

쉘 수준에서 이는 단순히 스크립트/함수/명령이 다른 스크립트/함수/명령에 인수로 전달된다는 의미입니다.

이제 간단한 예로 다음 스크립트를 살펴보겠습니다.

$ cat ~/w/bin/x
#! /bin/bash
cmd=$1; shift
case $1 in *%*) flt=${1//\%/\'%s\'};; *) flt="$1 '%s'";; esac; shift
q="'\\''"; f=${flt//\\/'\\'}; p=`printf "<($f) " "${@//\'/$q}"`
eval "$cmd" "$p"

개요가 있습니다

x command filter [file ...]

filter각 인수에 적용된 다음 file필터 command의 출력을 인수로 사용하여 호출됩니다.

예를 들어:

x diff zcat a.gz b.bz   # diff gzipped files
x diff3 zcat a.gz b.gz c.gz   # same with three-way diff
x diff hd a b  # hex diff of binary files
x diff 'zcat % | sort -u' a.gz b.gz  # first uncompress the files, then sort+uniq them, then compare them
x 'comm -12' sort a b  # find common lines in unsorted files

이것은 lisp에서 할 수 있는 것과 매우 유사합니다(농담입니다 ;-))

어떤 사람들은 "콜백" 용어를 "이벤트 핸들러" 및/또는 "클로저"(함수 + 데이터/환경 튜플)로 제한해야 한다고 주장합니다.일반적으로 말하면 수락됨중요성. 좁은 의미의 "콜백"이 셸에서 많이 사용되지 않는 이유 중 하나는 파이프라인 + 병렬성 + 동적 프로그래밍이 훨씬 더 강력하고, 쉘은 perlor 의 투박한 버전 입니다 python.

답변4

다른 답변에 몇 마디만 추가하면 됩니다. 함수 콜백은 콜백 함수 외부의 함수에서 작동합니다. 이를 달성하려면 콜백할 함수의 전체 정의를 콜백 함수에 전달하거나 해당 코드를 콜백 함수에서 사용할 수 있어야 합니다.

전자(다른 함수에 코드 전달)도 가능하지만 복잡한 문제 때문에 예제를 건너뛰겠습니다. 후자(이름으로 함수 전달)는 일반적인 관행입니다. 함수 범위 외부에서 선언된 변수와 함수는 정의가 해당 함수에 대한 호출보다 앞에 있는 한 해당 함수에서 사용할 수 있기 때문입니다(역으로, 앞에 선언됨). 부름).

또한 함수를 내보낼 때도 비슷한 상황이 발생한다는 점에 유의하세요. 함수를 가져오는 쉘은 이미 프레임을 준비하고 함수 정의가 이를 실행하기를 기다리고 있습니다. 함수 내보내기는 Bash에 존재하며 이전에는 심각한 문제를 일으켰습니다(Shellshock이라고 함).

Bash에서 명시적으로 제공되지 않는 다른 함수에 함수를 전달하는 다른 방법으로 이 답변을 마무리하겠습니다. 이 메서드는 이름이 아닌 주소로 전달됩니다. 이 방법은 예를 들어 Perl에서 찾을 수 있습니다. Bash는 함수나 변수에 대해 이 방법을 제공하지 않습니다. 그러나 Bash의 예를 사용하여 상황을 더 완벽하게 이해하려면 함수 코드가 메모리 어딘가에 있을 수 있으며 해당 메모리 위치(주소라고 함)에서 액세스할 수 있다는 점을 알아야 합니다. 코드.

관련 정보