"콜백"프로그래밍 개념이 Bash에 존재합니까?


21

프로그래밍에 대해 읽을 때 몇 번이나 "콜백"개념을 발견했습니다.

놀랍게도, 나는이 용어 "콜백 함수"에 대해 "didactic"또는 "clear"라고 부를 수있는 설명을 찾지 못했다.

"콜백"프로그래밍 개념이 Bash에 존재합니까? 그렇다면 작고 ​​간단한 Bash 예제로 대답하십시오.


2
"콜백"은 실제 개념입니까, 아니면 "일류 함수"입니까?
Cedric H.

당신은 찾을 수 있습니다 declarative.bash명시 적으로 주어진 값이 필요할 때 호출되도록 구성 기능을 활용하는 프레임 워크로, 흥미 롭군요.
Charles Duffy

다른 관련 프레임 워크 : bashup / events . 이 문서에는 검증, 조회 등과 같은 콜백 사용 데모가 많이 포함되어 있습니다.
PJ Eby

1
@CedricH. 당신에게 투표했습니다. "콜백"은 실제 개념입니까? 아니면 "일류 함수"입니까? "다른 질문으로 물어 보는 것이 좋은 질문입니까?
prosody-Gab Vereable Context

콜백은 "주어진 이벤트가 트리거 된 후 다시 호출되는 함수"를 의미합니다. 그 맞습니까?
JohnDoea

답변:


44

일반적인 명령형 프로그래밍 에서는 일련의 명령어를 작성하고 명시적인 제어 흐름으로 차례로 실행됩니다. 예를 들면 다음과 같습니다.

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

기타

예제에서 볼 수 있듯이 명령형 프로그래밍에서는 실행 흐름을 매우 쉽게 따르며 주어진 코드 줄에서 항상 실행 컨텍스트를 결정하고 실행 명령을 수행하여 명령의 결과로 실행된다는 것을 알고 흐름의 위치 (또는 함수를 작성중인 경우 해당 호출 사이트의 위치)

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

콜백을 사용하는 경우 "지리적으로"일련의 지침을 사용하는 대신 언제 호출해야하는지 설명합니다. 다른 프로그래밍 환경의 일반적인 예는 "이 리소스 다운로드 및 다운로드가 완료되면이 콜백을 호출하십시오"와 같은 경우입니다. Bash에는 이런 종류의 일반적인 콜백 구조가 없지만 오류 처리 및 기타 상황에 대한 콜백이 있습니다 . 예를 들어 (이 예제를 이해하려면 먼저 명령 대체 및 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함수를 호출하지 않습니다 . 그것은 사용을 호출 할 때 배쉬를 알려줍니다 trap cleanup EXIT, "를 실행하십시오, 사랑 배쉬 cleanup당신이 종료 할 때 명령을"(그리고 cleanup앞에서 정의 된 함수로 발생하지만 배쉬 이해 무엇이든 될 수있다). 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합니다. 데이터 (나머지 인수)뿐만 아니라 동작 (첫 번째 인수)도 제공합니다.

(두 번째 예에서 설명 된 유스 케이스에서는 "콜백"이라는 용어를 사용하지 않을 것이지만 아마도 내가 사용하는 언어로 인한 습관 일 수 있습니다. 함수 나 람다를 전달하는 것으로 생각합니다. 이벤트 지향 시스템에서 콜백을 등록하는 대신.)


25

먼저 함수를 콜백 함수로 만드는 것은 기능이 아니라 사용 방식이라는 점에 유의해야합니다. 콜백은 작성한 코드가 작성하지 않은 코드에서 호출되는 경우입니다. 특정 이벤트가 발생하면 시스템에 전화를 걸어달라고 요청합니다.

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

콜백의 또 다른 예 -execfind명령 의 동작입니다 . find명령 의 역할은 디렉토리를 재귀 적으로 순회하고 각 파일을 차례로 처리하는 것입니다. 기본적으로 처리는 파일 이름 (implicit -print) 을 인쇄하는 것이지만 -exec처리는 지정한 명령을 실행하는 것입니다. 콜백은 콜백 정의에 적합하지만 콜백은 별도의 프로세스에서 실행되므로 매우 유연하지 않습니다.

찾기 유사 함수를 구현 한 경우 콜백 함수를 사용하여 각 파일을 호출 할 수 있습니다. 다음은 함수 이름 (또는 외부 명령 이름)을 인수로 사용하여 현재 디렉토리와 해당 서브 디렉토리의 모든 일반 파일에서 호출하는 매우 단순화 된 찾기 유사 함수입니다. 이 함수는 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
}

쉘은 주로 간단한 프로그램을 위해 설계 되었기 때문에 다른 환경에서와 같이 쉘 프로그래밍에서는 콜백이 일반적이지 않습니다. 콜백은 데이터와 제어 흐름이 독립적으로 작성 및 배포되는 코드 부분 (기본 시스템, 다양한 라이브러리, 응용 프로그램 코드) 사이에서 앞뒤로 이동할 가능성이 높은 환경에서 더 일반적입니다.


1
특히 잘 설명
roaima

1
@ JohnDoea 아이디어는 실제로 작성하는 기능이 아니라는 점에서 매우 단순화되었다고 생각합니다. 그러나 아마 더 간단한 예에 콜백을 실행하기 위해 하드 코딩 된 목록과 함께 뭔가 될 것이다 : foreach_server() { declare callback="$1"; declare server; for server in 192.168.0.1 192.168.0.2 192.168.0.3; do "$callback" "$server"; done; }당신이로 실행할 수있는 foreach_server echo, foreach_server nslookupdeclare callback="$1"약 비록이 얻을 수 간단하게 : 콜백이 곳에서 전달 될 수있다, 또는 콜백이 아닙니다.
IMSoP

4
'콜백은 작성하지 않은 코드에서 작성하는 코드를 호출하는 것입니다.' 단순히 잘못되었습니다. 비 블로킹 비동기 작업을 수행하는 작업을 작성하고 완료되면 실행될 콜백으로 실행할 수 있습니다. 코드를 작성한 사람과는 관련이 없습니다.
mikemaccana

5
@mikemaccana 물론 같은 사람이 코드의 두 부분을 썼을 수도 있습니다. 그러나 일반적인 경우는 아닙니다. 공식적인 정의를 제공하지 않고 개념의 기본 사항을 설명하고 있습니다. 모든 경우에 대해 설명하면 기본 사항을 전달하기가 어렵습니다.
Gilles 'SO- 악마 그만'

1
그것을 듣고 기뻐. 콜백과 콜백을 사용하는 코드를 작성하는 사람들은 일반적이지 않거나 가장자리가 아니며 혼란으로 인해이 답변이 기본 사항을 전달한다는 데 동의하지 않습니다.
mikemaccana

7

"콜백"은 다른 함수에 인수로 전달 된 함수입니다.

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

이제 간단한 예를 들어 다음 스크립트를 고려하십시오.

$ 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 ...]

filterfile인수에 적용한 다음 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에서 할 수있는 일에 매우 가깝습니다 (농담 ;-)).

어떤 사람들은 "콜백"용어를 "이벤트 핸들러"및 / 또는 "클로저"(함수 + 데이터 / 환경 튜플)로 제한해야한다고 주장합니다. 이것은 결코 일반적으로 받아 들여지는 의미 가 아닙니다 . 그리고 좁은 의미에서 "콜백"이 쉘에서 많이 사용되지 않는 한 가지 이유는 파이프 + 병렬 처리 + 동적 프로그래밍 기능이 훨씬 강력하기 때문에 이미 성능 측면에서 비용을 지불하고 있기 때문입니다. 셸을 복잡한 버전의 perl또는 로 사용하십시오 python.


귀하의 예제가 꽤 유용 해 보이지만 bash 매뉴얼을 열어서 어떻게 작동하는지 알아 내야한다는 것은 상당히 조밀합니다 (그리고 수년 동안 가장 간단한 bash로 작업했습니다). lisp. ;)
Joe

1
@Joe 입력 파일이 두 개이고 %필터에 보간이 없는 것이 괜찮다 면 모든 것이 다음과 같이 줄어들 수 있습니다 cmd=$1; shift; flt=$1; shift; $cmd <($flt "$1") <($flt "$2"). 그러나 그것은 훨씬 덜 유용하고 예시적인 imho입니다.
mosvy

1
또는 더 나은$1 <($2 "$3") <($2 "$4")
mosvy

+1 감사합니다. 당신의 의견과 그것을 쳐다보고 한동안 코드를 가지고 노는 것이 나를 위해 그것을 명확하게했습니다. 나는 또한 내가 영원히 사용했던 무언가에 대해 "문자열 보간"이라는 새로운 용어를 배웠다.
Joe

4

거의.

bash에서 콜백을 구현하는 간단한 방법 중 하나는 "콜백 함수"역할을하는 프로그램 이름을 매개 변수로 받아들이는 것입니다.

# This is script worker.sh accepts a callback in $1
cb="$1"
....
# Execute the call back, passing 3 parameters
$cb foo bar baz

이것은 다음과 같이 사용됩니다 :

# Invokes mycb.sh as a callback
worker.sh mycb.sh

물론 bash에는 클로저가 없습니다. 따라서 콜백 함수는 호출자 측의 변수에 액세스 할 수 없습니다. 그러나 콜백에 필요한 데이터를 환경 변수에 저장할 수 있습니다. 콜백에서 호출자 스크립트로 정보를 다시 전달하는 것이 더 까다 롭습니다. 데이터를 파일에 넣을 수 있습니다.

디자인에서 모든 프로세스가 단일 프로세스에서 처리되도록 허용하면 콜백에 쉘 함수를 사용할 수 있으며이 경우 콜백 함수는 호출자 측의 변수에 액세스 할 수 있습니다.


3

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

전자 (코드를 다른 함수에 전달)가 가능하지만 복잡성을 포함하는 예제를 건너 뛸 것입니다. 후자 (이름으로 함수를 전달 함)는 하나의 함수 범위 밖에서 선언 된 변수와 함수가 해당 함수에서 작동하는 함수를 호출하기 전에 해당 함수에서 해당 함수에서 사용할 수 있으므로 일반적입니다. 호출하기 전에 선언해야합니다).

또한 함수를 내보낼 때도 비슷한 현상이 발생합니다. 함수를 가져 오는 셸은 프레임 워크가 준비되어 있고 함수 정의가 동작하기를 기다리고있을뿐입니다. 함수 내보내기가 Bash에 있으며 이전에 심각한 문제인 btw (Shellshock)를 발생 시켰습니다.

이 답변은 Bash에 명시 적으로 존재하지 않는 다른 함수에 함수를 전달하는 또 다른 방법으로 완료합니다. 이것은 이름이 아닌 주소로 전달합니다. 예를 들어 이것은 Perl에서 찾을 수 있습니다. Bash는 함수 나 변수를 위해이 방법을 제공하지 않습니다. 그러나 당신이 말한 것처럼 Bash를 예로 들어 더 넓은 그림을 원한다면 함수 코드가 메모리 어딘가에있을 수 있으며 해당 메모리 위치에서 해당 코드에 액세스 할 수 있다는 것을 알아야합니다. 그것의 주소를 불렀다.


2

bash에서 콜백의 가장 간단한 예 중 하나는 많은 사람들이 익숙하지만 실제로 어떤 디자인 패턴을 사용하고 있는지 모르는 것입니다.

크론

Cron을 사용하면 일부 조건 (시간 사양)이 충족 될 때 cron 프로그램이 호출 할 실행 파일 (바이너리 또는 스크립트)을 지정할 수 있습니다.

라는 스크립트가 있다고 가정하십시오 doEveryDay.sh. 스크립트를 작성하는 비 콜백 방법은 다음과 같습니다.

#! /bin/bash
while true; do
    doSomething
    sleep $TWENTY_FOUR_HOURS
done

콜백 방법은 다음과 같습니다.

#! /bin/bash
doSomething

그런 다음 crontab에서 다음과 같이 설정했습니다.

0 0 * * *     doEveryDay.sh

그런 다음 이벤트가 트리거 될 때까지 코드를 작성하지 않아도되지만 대신 cron코드를 다시 호출해야합니다.


이제이 코드를 bash로 작성하는 방법을 고려하십시오.

bash에서 다른 스크립트 / 기능을 어떻게 실행 하시겠습니까?

함수를 작성해 봅시다 :

function every24hours () {
    CALLBACK=$1 ;# assume the only argument passed is
                 # something we can "call"/execute
    while true; do
        $CALLBACK ;# simply call the callback
        sleep $TWENTY_FOUR_HOURS
    done
}

콜백을 받아들이는 함수를 만들었습니다. 다음과 같이 간단히 호출 할 수 있습니다.

# "ping" google website every day
every24hours 'curl google.com'

물론 24 시간마다 함수가 반환되지 않습니다. Bash는 다음을 추가하여 매우 쉽게 비 동기화하고 프로세스를 생성 할 수 있다는 점에서 약간 독특합니다 &.

every24hours 'curl google.com' &

이것을 함수로 원하지 않으면 대신 스크립트로 수행 할 수 있습니다.

#every24hours.sh
CALLBACK=$1 ;# assume the only argument passed is
               # something we can "call"/execute
while true; do
    $CALLBACK ;# simply call the callback
    sleep $TWENTY_FOUR_HOURS
done

보시다시피, bash의 콜백은 사소합니다. 간단합니다 :

CALLBACK_SCRIPT=$3 ;# or some other 
                    # argument to 
                    # function/script

콜백을 호출하는 것은 간단합니다.

$SOME_CALLBACK_FUNCTION_OR_SCRIPT

위의 양식에서 볼 수 있듯이 콜백은 언어의 직접적인 특징이 아닙니다. 일반적으로 기존 언어 기능을 사용하여 창의적인 방식으로 프로그래밍합니다. 일부 코드 블록 / 함수 / 스크립트의 포인터 / 참조 / 복사를 저장할 수있는 모든 언어는 콜백을 구현할 수 있습니다.


프로그램의 다른 예 / 콜백을 포함 동의 스크립트 watchfind(사용 -exec매개 변수)
slebetman

0

콜백은 일부 이벤트가 발생할 때 호출되는 함수입니다. 을 사용하면 bash신호 처리, 쉘 종료 및 쉘 오류 이벤트, 디버그 이벤트 및 함수 / 소스 스크립트 리턴 이벤트와 관련된 유일한 이벤트 처리 메커니즘이 있습니다.

다음은 신호 트랩을 활용하는 쓸모 없지만 간단한 콜백의 예입니다.

먼저 콜백을 구현하는 스크립트를 작성하십시오.

#!/bin/bash

myCallback() {
    echo "I've been called at $(date +%Y%m%dT%H%M%S)"
}

# Set the handler
trap myCallback SIGUSR1

# Main loop. Does nothing useful, essentially waits
while true; do
    read foo
done

그런 다음 한 터미널에서 스크립트를 실행하십시오.

$ ./ 콜백 예

다른 하나에서는 USR1신호를 쉘 프로세스로 보냅니다 .

$ pkill -USR1 callback-example

전송 된 각 신호는 첫 번째 터미널에서 다음과 같은 라인 표시를 트리거해야합니다.

I've been called at 20180925T003515
I've been called at 20180925T003517

ksh93, bash나중에 채택 된 많은 기능을 구현하는 쉘로서 "훈련 기능"이라고하는 기능을 제공합니다. 로 사용할 수없는 이러한 함수 bash는 쉘 변수가 수정되거나 참조 될 때 호출됩니다 (예 : 읽기). 이것은 더 흥미로운 이벤트 중심 응용 프로그램으로가는 길을 열어줍니다.

예를 들어,이 기능을 사용하면 그래픽 위젯의 X11 / Xt / Motif 스타일 콜백을 ksh이라는 그래픽 확장이 포함 된 이전 버전에서 구현할 수 있습니다 dtksh. dksh manual을 참조하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.