프로그래밍에 대해 읽을 때 몇 번이나 "콜백"개념을 발견했습니다.
놀랍게도, 나는이 용어 "콜백 함수"에 대해 "didactic"또는 "clear"라고 부를 수있는 설명을 찾지 못했다.
"콜백"프로그래밍 개념이 Bash에 존재합니까? 그렇다면 작고 간단한 Bash 예제로 대답하십시오.
declarative.bash
명시 적으로 주어진 값이 필요할 때 호출되도록 구성 기능을 활용하는 프레임 워크로, 흥미 롭군요.
프로그래밍에 대해 읽을 때 몇 번이나 "콜백"개념을 발견했습니다.
놀랍게도, 나는이 용어 "콜백 함수"에 대해 "didactic"또는 "clear"라고 부를 수있는 설명을 찾지 못했다.
"콜백"프로그래밍 개념이 Bash에 존재합니까? 그렇다면 작고 간단한 Bash 예제로 대답하십시오.
declarative.bash
명시 적으로 주어진 값이 필요할 때 호출되도록 구성 기능을 활용하는 프레임 워크로, 흥미 롭군요.
답변:
일반적인 명령형 프로그래밍 에서는 일련의 명령어를 작성하고 명시적인 제어 흐름으로 차례로 실행됩니다. 예를 들면 다음과 같습니다.
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
책임은 백업 디렉토리에 (단독) 인수의 사본을 작성하는 것입니다. 모두 doonall
와 backup
등 더 많은 코드를 다시 사용할 수 있습니다 다른 상황, 더 나은 테스트에 사용 할 수 있습니다
이 경우 콜백은 backup
함수로서 doonall
다른 각 인수에 대해 "콜백"하도록 doonall
합니다. 데이터 (나머지 인수)뿐만 아니라 동작 (첫 번째 인수)도 제공합니다.
(두 번째 예에서 설명 된 유스 케이스에서는 "콜백"이라는 용어를 사용하지 않을 것이지만 아마도 내가 사용하는 언어로 인한 습관 일 수 있습니다. 함수 나 람다를 전달하는 것으로 생각합니다. 이벤트 지향 시스템에서 콜백을 등록하는 대신.)
먼저 함수를 콜백 함수로 만드는 것은 기능이 아니라 사용 방식이라는 점에 유의해야합니다. 콜백은 작성한 코드가 작성하지 않은 코드에서 호출되는 경우입니다. 특정 이벤트가 발생하면 시스템에 전화를 걸어달라고 요청합니다.
쉘 프로그래밍에서 콜백의 예는 트랩입니다. 트랩은 함수로 표현되지 않고 평가할 코드 조각으로 표현되는 콜백입니다. 쉘이 특정 신호를 수신하면 쉘이 코드를 호출하도록 요청합니다.
콜백의 또 다른 예 -exec
는 find
명령 의 동작입니다 . 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
}
쉘은 주로 간단한 프로그램을 위해 설계 되었기 때문에 다른 환경에서와 같이 쉘 프로그래밍에서는 콜백이 일반적이지 않습니다. 콜백은 데이터와 제어 흐름이 독립적으로 작성 및 배포되는 코드 부분 (기본 시스템, 다양한 라이브러리, 응용 프로그램 코드) 사이에서 앞뒤로 이동할 가능성이 높은 환경에서 더 일반적입니다.
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 nslookup
등 declare callback="$1"
약 비록이 얻을 수 간단하게 : 콜백이 곳에서 전달 될 수있다, 또는 콜백이 아닙니다.
"콜백"은 다른 함수에 인수로 전달 된 함수입니다.
쉘 수준에서 이는 단순히 다른 스크립트 / 함수 / 명령에 인수로 전달 된 스크립트 / 함수 / 명령을 의미합니다.
이제 간단한 예를 들어 다음 스크립트를 고려하십시오.
$ 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에서 할 수있는 일에 매우 가깝습니다 (농담 ;-)).
어떤 사람들은 "콜백"용어를 "이벤트 핸들러"및 / 또는 "클로저"(함수 + 데이터 / 환경 튜플)로 제한해야한다고 주장합니다. 이것은 결코 일반적으로 받아 들여지는 의미 가 아닙니다 . 그리고 좁은 의미에서 "콜백"이 쉘에서 많이 사용되지 않는 한 가지 이유는 파이프 + 병렬 처리 + 동적 프로그래밍 기능이 훨씬 강력하기 때문에 이미 성능 측면에서 비용을 지불하고 있기 때문입니다. 셸을 복잡한 버전의 perl
또는 로 사용하십시오 python
.
%
필터에 보간이 없는 것이 괜찮다 면 모든 것이 다음과 같이 줄어들 수 있습니다 cmd=$1; shift; flt=$1; shift; $cmd <($flt "$1") <($flt "$2")
. 그러나 그것은 훨씬 덜 유용하고 예시적인 imho입니다.
$1 <($2 "$3") <($2 "$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에는 클로저가 없습니다. 따라서 콜백 함수는 호출자 측의 변수에 액세스 할 수 없습니다. 그러나 콜백에 필요한 데이터를 환경 변수에 저장할 수 있습니다. 콜백에서 호출자 스크립트로 정보를 다시 전달하는 것이 더 까다 롭습니다. 데이터를 파일에 넣을 수 있습니다.
디자인에서 모든 프로세스가 단일 프로세스에서 처리되도록 허용하면 콜백에 쉘 함수를 사용할 수 있으며이 경우 콜백 함수는 호출자 측의 변수에 액세스 할 수 있습니다.
다른 답변에 몇 마디 만 추가하면됩니다. 함수 콜백은 콜백하는 함수 외부의 함수에서 작동합니다. 이를 위해서는 콜백 할 함수의 전체 정의를 콜백하는 함수에 전달하거나 콜백 할 수있는 코드를 사용해야합니다.
전자 (코드를 다른 함수에 전달)가 가능하지만 복잡성을 포함하는 예제를 건너 뛸 것입니다. 후자 (이름으로 함수를 전달 함)는 하나의 함수 범위 밖에서 선언 된 변수와 함수가 해당 함수에서 작동하는 함수를 호출하기 전에 해당 함수에서 해당 함수에서 사용할 수 있으므로 일반적입니다. 호출하기 전에 선언해야합니다).
또한 함수를 내보낼 때도 비슷한 현상이 발생합니다. 함수를 가져 오는 셸은 프레임 워크가 준비되어 있고 함수 정의가 동작하기를 기다리고있을뿐입니다. 함수 내보내기가 Bash에 있으며 이전에 심각한 문제인 btw (Shellshock)를 발생 시켰습니다.
이 답변은 Bash에 명시 적으로 존재하지 않는 다른 함수에 함수를 전달하는 또 다른 방법으로 완료합니다. 이것은 이름이 아닌 주소로 전달합니다. 예를 들어 이것은 Perl에서 찾을 수 있습니다. Bash는 함수 나 변수를 위해이 방법을 제공하지 않습니다. 그러나 당신이 말한 것처럼 Bash를 예로 들어 더 넓은 그림을 원한다면 함수 코드가 메모리 어딘가에있을 수 있으며 해당 메모리 위치에서 해당 코드에 액세스 할 수 있다는 것을 알아야합니다. 그것의 주소를 불렀다.
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
위의 양식에서 볼 수 있듯이 콜백은 언어의 직접적인 특징이 아닙니다. 일반적으로 기존 언어 기능을 사용하여 창의적인 방식으로 프로그래밍합니다. 일부 코드 블록 / 함수 / 스크립트의 포인터 / 참조 / 복사를 저장할 수있는 모든 언어는 콜백을 구현할 수 있습니다.
watch
및 find
(사용 -exec
매개 변수)
콜백은 일부 이벤트가 발생할 때 호출되는 함수입니다. 을 사용하면 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을 참조하십시오 .