AppleScript 첫걸음

✍🏼 작성일 2022년 05월 03일    💡 수정일 2023년 01월 05일
❗️ 참고: 이 글이 작성된 지 이미 일이 지났습니다. 시의성에 유의하세요
🖥  설명:AppleScript를 간단히 체험해보고 일반적으로 사용되는 사용자 상호작용 방식을 소개하면서 매우 강력하다는 느낌을 받았습니다.
📚  Craft에도 게시: https://www.craft.do/s/RsgimVt9VBxAmg

서문

최근 M1 Max 칩과 64GB 메모리가 탑재된 Mac Studio를 새로 구입했습니다. 성능이 매우 뛰어납니다. 그동안 블로그 처리 과정]에서 Craft에서 가져온 이미지(이 단계는 두 서비스 모두 해외에 있어서 빠릅니다)를 텐센트 클라우드 COS로 전송할 때 Github Action을 사용하면서 네트워크 속도가 느린 문제가 자주 발생했습니다.

SCR-20220502-q47

조사해 본 결과, Github는 Action 서버의 지역을 설정하는 기능을 지원하지 않는 것]으로 보입니다. 이에 따라 이후 배포 프로세스를 로컬로 옮겨 빌드 속도를 높이기로 결정했고, 이 글을 작성하게 되었습니다. 아래에서 AppleScript는 AS로 줄여서 표기합니다.

왜 다른 도구가 아닌 AppleScript를 사용했나요?

우선, 가능한 한 작업 경로와 복잡도를 최소화하고 싶었기 때문에 다음과 같은 작업 단계를 정했습니다:

  1. Craft의 게시 버튼을 클릭합니다.

  2. 로컬에서 빌드하며, 터미널에 빌드 로그를 표시합니다.

  3. 빌드 결과를 github로 푸시합니다.

한 앱에서 다른 앱을 호출해야 하기 때문에 AppleScript가 유일한 선택지였습니다. 하지만 AS를 사용하더라도 실제로 실행되는 것은 기존의 js 파일이며, craft_publish_ci의 workflow 작업]을 로컬로 옮겨 실행하는 것뿐입니다.

AppleScript 소개

개념도:

execute_script_2x

AppleScript로 할 수 있는 것들

  1. 사용자와 상호작용할 수 있습니다. 예를 들어 사용자 입력에 응답하거나, 알림 센터를 통해 알림을 보내는 등이 가능합니다.

  2. 다른 앱을 제어할 수 있습니다. 다른 앱이 관련 AS 인터페이스를 제공하면 직접 제어할 수 있고, 그렇지 않은 경우 AS 문법을 사용해 인터페이스의 버튼 등을 조작해야 합니다.

  3. 자동화 작업을 수행할 수 있습니다. 이메일 보내기, 정해진 시간에 스크립트 실행, 음악 플레이어 열기, 화면 잠금, 사용자 정의 URL Scheme에 응답해 스크립트 실행 등이 가능합니다.

기본 문법

  • 각 줄 앞에 ---(하이픈 세 개) 또는 #를 추가하면 해당 줄을 주석 처리합니다.

  • (*``*xxx**``) (괄호+별표)로 감싼 xxx는 블록 주석으로 xxx의 내용을 주석 처리합니다.

  • 나머지는 기본적으로 구어체와 유사하지만, 영어 문법에 자신이 없다면 besides, against, over 등이 특정 문맥에서 어떤 의미인지 모를 경우 기본 문법을 사용하는 것이 좋습니다.

AppleScript의 형태

AppleScript는 일반적으로 Script(스크립트)와 Application(애플리케이션) 두 가지 형태로 사용됩니다. 여기서는 이 두 가지만 소개하며, Script bundle과 Text는 사용하지 않았으므로 다루지 않겠습니다.

SCR-20220502-qqc

참고: 동일한 코드라도 스크립트로 저장해 실행하는 것과 애플리케이션으로 저장해 실행하는 것은 인터페이스가 다릅니다.

스크립트

스크립트는 js와 같은 실행 가능한 파일이지만, as는 Mac 데스크톱 환경에서 실행되고 js는 Chrome에서 실행된다는 차이가 있습니다. 더블 클릭하면 실행됩니다. 아래는 스크립트 process가 진행 상황을 표시하는 인터페이스로, 스크립트 우측 상단의 ‘실행’ 버튼을 클릭해 실행합니다:

SCR-20220502-r3e

애플리케이션

애플리케이션으로 저장한 AS의 확장자는 Safari, Chrome, WeChat 등의 일반 애플리케이션과 동일하게 .app로 끝나며, 패키지 내용을 확인할 수도 있습니다. URL Scheme 호출, 더블 클릭, 콘텐츠를 앱 아이콘으로 드래그 앤 드롭 등 다양한 실행 방식이 있으며, 관련 이벤트 함수를 작성해 사용자 작업에 응답해야 합니다. 아래는 애플리케이션 process가 진행 상황을 표시하는 인터페이스로, 'xxx.app’을 더블 클릭해 실행합니다:

SCR-20220502-r33

기타

위의 두 가지 형태 외에는 자주 사용되지 않지만, 스크립트를 서비스로 저장해 상태 표시줄이나 모든 인터페이스의 오른쪽 클릭 메뉴에서 호출할 수 있도록 하려면 다른 형태가 필요합니다:

SCR-20220502-rcr

스크립트를 시스템 서비스로 저장할 수 있으며, 이 경우 process 코드가 실행될 때 진행률 표시줄이 상단 상태 표시줄에 표시됩니다:

scriptmenu_progress_2x

자주 사용하는 작업 코드

다음 작업은 ‘애플리케이션’ 유형의 AS를 기반으로 합니다. 아래 문법은 터미널에서 osascript -e '语法内容'을 사용해 빠르게 확인할 수 있습니다. 다만, process 진행률 표시줄과 같은 여러 줄 표현은 osascript로는 구현할 수 없습니다. tell와 같은 여러 줄 명령어에서 여러 -e 매개변수를 사용해 osascript -e ‘tell application “Finder”’ -e ‘end tell’처럼 연결할 수는 있지만, 이 문법은 process 진행률 표시줄에는 적용되지 않습니다.

사용자 상호작용

알림 센터에 메시지 표시

1
display notification "通知内容" with title "通知 title" subtitle "通知副标题"

SCR-20220502-sfl

팝업 창

1
display dialog "这是个通知"

Image

사용자 입력 받기

1
display dialog "What's your name?" default answer "" with icon note buttons {"取消", "确认"} default button "确认"

Image

참고: AS는 양식과 같은 컴포넌트를 생성할 수 없으며, 위와 같은 대화 상자만 생성할 수 있습니다. 웹의 prompt와 유사합니다.

주어진 텍스트 재생

1
say "What is your name?" using "Alex" speaking rate 140 pitch 42 modulation 60

기타

사용자에게 폴더 선택, 파일 선택, 색상 선택, 목록에서 항목 선택 등을 요청하는 기능은 일단 생략합니다.

명령어 실행

1
do shell script "echo $PATH"

주의: 명령어 실행 시 PATH 환경 변수는 /usr/bin:/bin:/usr/sbin:/sbin로 설정되어 있으므로, node, nvm 등 나중에 설치된 명령어를 실행할 수 없습니다. 이러한 명령어를 실행할 때는 명령어의 위치를 수동으로 지정해야 합니다.

node 스크립트 실행:

1
2
3
set node to "/Users/x/.nvm/versions/node/v14.19.1/bin/node"
set appPath to "/Applications/Xhelper.app/Contents/Resources/Scripts/"
do shell script node & " " & appPath & "index.js"

이전 명령어의 출력 결과 가져오기

직전 명령어 뒤에 바로 result를 사용하여 결과를 나타낼 수 있습니다:

1
2
do shell script "echo $PATH"
display dialog result with title "通知"

Image

디버그 정보 표시

log 문을 사용하면 됩니다. 그런 다음 Script Editor 아래의 Replies에서 출력 정보를 확인할 수 있습니다. 여기서 shell script 명령어 실행 중 스크립트가 콘솔에 출력한 내용을 볼 수 있으며, 예를 들어 js 스크립트 실행 시의 console.log 등이 있습니다.

1
log do shell script "echo $PATH"

SCR-20220502-sov

진행 상태 표시

진행 상태를 표시하려면 progress를 사용하며, 문법이 상대적으로 복잡합니다. 다양한 상태의 진행률을 표시해야 하므로 여러 줄이 필요합니다:

1
2
3
4
5
6
7
8
9
set progress total steps to 3
set progress completed steps to 0
set progress description to "处理中..."
delay 1
set progress completed steps to 1
delay 1
set progress completed steps to 2
delay 1
set progress completed steps to 3

관련 스크린샷은 위에서 확인할 수 있습니다.

일정 시간 지연 후 실행

1
delay n

n는 초 단위를 나타내며, 소수를 지원합니다. 일반적으로 사용자 작업을 시뮬레이션할 때 지연 작업으로 사용되며, 그렇지 않으면 스크립트 작업이 너무 빨라 페이지가 아직 표시되지 않을 수 있습니다. 일부 이상한 문제도 지연 실행으로 해결할 수 있으며, 이 문은 jssetTimeout 방법과 유사합니다.

다른 애플리케이션 호출

주의: 시스템 경고가 발생하며, 실행 전 확인이 필요합니다:

Image

확인 후 设置-安全与隐私-隐私-自动化에서 확인할 수 있습니다:

Image

다른 애플리케이션을 호출하여 해당 작업을 실행할 수 있습니다. 아래는 Terminal 애플리케이션을 실행한 후 활성화하고 명령어를 실행하는 예시입니다:

1
2
3
4
5
tell application "Terminal"
if not (exists window 1) then reopen
activate
do script "echo $PATH" in window 1
end tell

이 문은 첫 번째로 열린 창이 존재하면 활성화한 후 명령어를 실행하고(do script), 존재하지 않으면 새 창을 생성한 후 명령어를 실행합니다. 각 do script은 새로운 Terminal을 열기 때문에 이 확인은 창 재사용을 보장합니다. 그러나 window 1 내에 이미 실행 중인 프로세스(예: 서버가 중단되지 않은 경우)가 있다면 이 명령어는 실행되지 않으며, 추가 확인이 필요합니다. 여기서는 자세히 다루지 않습니다.

Image

URL Scheme 응답

때로는 AS 스크립트를 먼저 찾아서 더블 클릭하여 실행하고 싶지 않거나, 다른 애플리케이션에서 작성한 AS 애플리케이션을 호출하고 싶을 때 URL Scheme를 사용합니다. 이 글]에서 소개하고 있으니 참고하세요.

먼저 Info.plist 파일을 수정해야 합니다. Script Editor의 오른쪽에서 "Finder에서 보기"를 우클릭하면 됩니다:

Image

그런 다음 상위 폴더로 이동하여 해당 파일을 찾을 수 있습니다:

Image

내용은 XML과 유사하며, 다음과 같은 필드를 추가합니다:

1
2
3
4
5
6
7
8
9
10
<array>
<dict>
<key>CFBundleURLName</key>
<string>Open File</string>
<key>CFBundleURLSchemes</key>
<array>
<string>xhelper</string>
</array>
</dict>
</array>

그런 다음 XCode로 해당 파일을 열어 다음과 유사한 항목이 있는지 확인합니다:

Image

추가가 성공했음을 나타냅니다.

이 작업은 AS 애플리케이션이 xhelper(기존과 중복되지 않도록 원하는 접두사로 수정 가능)로 시작하는 URL에 응답하도록 설정하는 것입니다.

설정 완료 후, Script Editor에서 저장하거나 cmd + L를 실행하거나 한 번 실행(또는 모두 수행)하여 다시 컴파일해야 합니다. 그런 다음 적용됩니다.

그런 다음 AS가 해당 URL에 응답하도록 이벤트 리스너를 설정해야 합니다:

1
2
3
on open location this_URL
display dialog this_URL
end open location

위 코드에서 this_URLxhelper로 열린 링크입니다.

이제 브라우저(Chrome)에서 링크를 테스트할 수 있습니다:

Image

그러면 AS 애플리케이션이 응답하여 xhelper://你好!을 팝업으로 표시합니다. 여기서는 encode 인코딩이 필요하지 않습니다.

사용 예시

여기서는 제가 사용하는 Craft build 코드를 공유합니다. 위에서 설명한 URL Scheme를 사용하며, node로 실행하는 js는 이전 워크플로]에서 개조한 것입니다. 새로운 로직은 없습니다. 또한 AS 문법은 구어체에 가깝기 때문에 각 문장의 의미를 바로 이해할 수 있습니다. 여기서는 일일이 설명하지 않겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
on open location this_URL
try
set startOffset to offset of "://" in this_URL
set the content to text from (startOffset + 3) to -1 of this_URL
set node to "/Users/x/.nvm/versions/node/v14.19.1/bin/node"
set appPath to "/Applications/Xhelper.app/Contents/Resources/Scripts/"
--- 如果有两个参数表示需要发布到 wechat,如果只有一个参数则参数即为 base64 编码的内容
set AppleScript's text item delimiters to "&"
set arguments to every text item of the content
set AppleScript's text item delimiters to ""
set listLength to the length of arguments
if listLength = 2 then
set realContent to item 2 of arguments
do shell script "echo " & realContent & " > " & appPath & "content.base64.txt"
tell application "Terminal"
if not (exists window 1) then reopen
activate
set alive to do script node & " " & appPath & "index.js" in window 1
--- 等待上一个脚本执行完毕后再执行下一个脚本
repeat
delay 0.1
if not busy of alive then exit repeat
end repeat
beep
do script node & " " & appPath & "wechat.js" in window 1
end tell
else
do shell script "echo " & content & " > " & appPath & "content.base64.txt"
tell application "Terminal"
if not (exists window 1) then reopen
activate
do script node & " " & appPath & "index.js" in window 1
end tell
end if
on error error_message
display dialog error_message buttons {"Cancel"} default button 1
end try

end open location

결과:

Image

참고 링크

- EOF -
이 글의 최초 게시: AppleScript 첫걸음 - Xheldon Blog