

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 教學課程：使用 AWS IoT 裝置用戶端示範遠端動作 （任務）
<a name="iot-dc-runjobs"></a>

在這些教學課程中，您要設定並部署任務至 Raspberry Pi，以此示範如何將遠程操作發送至 IoT 裝置。

**若要開始此教學課程：**
+ 將本機主機電腦和 Raspberry Pi 設定成[上一節](iot-dc-testconn.md)中的使用方式。
+ 如果您尚未完成上一節中的教學課程，您可以使用 Raspberry Pi 搭配 microSD 卡來嘗試本教學課程，該卡具有您在 中安裝 AWS IoT 裝置用戶端後儲存的影像[(選用) 儲存 microSD 卡映像](iot-dc-install-download.md#iot-dc-install-dc-save)。
+ 如果您之前已執行過此示範，請檢閱 [步驟 2：使用 AWS IoT 裝置用戶端建置示範 AWS 帳戶 後清除您的](iot-dc-cleanup.md#iot-dc-cleanup-cloud) 以刪除先前執行中建立的所有 AWS IoT 資源，以避免重複的資源錯誤。

此教學課程約需 45 分鐘方能完成。

**完成此主題時：**
+ 您將展示 IoT 裝置可以使用 AWS IoT Core 執行 管理之遠端操作的不同方式 AWS IoT 。

**必要設備：**
+ [上一節](iot-dc-install-dc.md)中測試的本機開發和測試環境
+ [上一節](iot-dc-install-dc.md)中測試的 Raspberry Pi
+ [上一節](iot-dc-install-dc.md)中測試的 Raspberry Pi microSD 記憶卡

**Topics**
+ [準備 Raspberry Pi 以執行任務](iot-dc-runjobs-prepare.md)
+ [AWS IoT 使用 AWS IoT 裝置用戶端在 中建立和執行任務](iot-dc-runjobs-prepare-define.md)

# 準備 Raspberry Pi 以執行任務
<a name="iot-dc-runjobs-prepare"></a>

本節中的程序說明如何使用 AWS IoT 裝置用戶端，讓您的 Raspberry Pi 準備好執行任務。

**注意**  
這些程序是裝置特定的程序。如果想要同時使用多個裝置執行本節中的程序，每個裝置都需要有自己的政策和唯一的裝置特定憑證及物件名稱。若要為每個裝置提供獨特資源，請在按程序所述變更裝置特定元素時，針對每個裝置執行此程序一次。

**Topics**
+ [佈建 Raspberry Pi 來示範任務](#iot-dc-runjobs-prepare-provision)
+ [設定 AWS IoT 裝置用戶端以執行任務代理程式](#iot-dc-runjobs-prepare-config)

## 佈建 Raspberry Pi 來示範任務
<a name="iot-dc-runjobs-prepare-provision"></a>

本節中的程序 AWS IoT 會透過為 Raspberry Pi 建立 AWS IoT 資源和裝置憑證，在 中佈建您的 Raspberry Pi。

**Topics**
+ [建立和下載裝置憑證檔案以示範 AWS IoT 任務](#iot-dc-runjobs-prepare-cert)
+ [建立 AWS IoT 資源以示範 AWS IoT 任務](#iot-dc-runjobs-prepare-iot)

### 建立和下載裝置憑證檔案以示範 AWS IoT 任務
<a name="iot-dc-runjobs-prepare-cert"></a>

此程序會建立此示範的裝置憑證檔案。

如果要準備一個以上的裝置，則必須在每個裝置上執行此程序。

**若要為 Raspberry Pi 建立並下載裝置憑證檔案：**

在連接至 Raspberry Pi 的本機主機電腦終端機視窗中，輸入這些命令。

1. 請輸入下列命令，為裝置建立裝置憑證檔案。

   ```
   aws iot create-keys-and-certificate \
   --set-as-active \
   --certificate-pem-outfile "~/certs/jobs/device.pem.crt" \
   --public-key-outfile "~/certs/jobs/public.pem.key" \
   --private-key-outfile "~/certs/jobs/private.pem.key"
   ```

   此命令會傳回類似以下的回應。儲存 `certificateArn` 值，供之後使用。

   ```
   {
   "certificateArn": "arn:aws:iot:us-west-2:57EXAMPLE833:cert/76e7e4edb3e52f52334be2f387a06145b2aa4c7fcd810f3aea2d92abc227d269",
   "certificateId": "76e7e4edb3e52f5233EXAMPLE7a06145b2aa4c7fcd810f3aea2d92abc227d269",
   "certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDWTCCAkGgAwIBAgI_SHORTENED_FOR_EXAMPLE_Lgn4jfgtS\n-----END CERTIFICATE-----\n",
   "keyPair": {
       "PublicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BA_SHORTENED_FOR_EXAMPLE_ImwIDAQAB\n-----END PUBLIC KEY-----\n",
       "PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQE_SHORTENED_FOR_EXAMPLE_T9RoDiukY\n-----END RSA PRIVATE KEY-----\n"
   }
   }
   ```

1. 輸入下列命令來設定憑證目錄及其檔案的許可。

   ```
   chmod 700 ~/certs/jobs
   chmod 644 ~/certs/jobs/*
   chmod 600 ~/certs/jobs/private.pem.key
   ```

1. 執行此命令來檢閱憑證目錄和檔案的許可。

   ```
   ls -l ~/certs/jobs
   ```

   命令的輸出應該與您在此處看到的相同，但檔案日期和時間會有所不同。

   ```
   -rw-r--r-- 1 pi pi 1220 Oct 28 13:02 device.pem.crt
   -rw------- 1 pi pi 1675 Oct 28 13:02 private.pem.key
   -rw-r--r-- 1 pi pi  451 Oct 28 13:02 public.pem.key
   ```

在下載裝置憑證檔案至 Raspberry Pi 之後，您就可以繼續進行 [佈建 Raspberry Pi 來示範任務](#iot-dc-runjobs-prepare-provision)。

### 建立 AWS IoT 資源以示範 AWS IoT 任務
<a name="iot-dc-runjobs-prepare-iot"></a>

建立此裝置 AWS IoT 的資源。

如果要準備一個以上的裝置，則必須為每個裝置上執行此程序。



**若要在 AWS IoT中佈建裝置：**

在連接至 Raspberry Pi 本機主機電腦上的終端機視窗中：

1. 輸入下列命令，取得 AWS 帳戶裝置資料端點地址。

   ```
   aws iot describe-endpoint --endpoint-type IoT:Data-ATS
   ```

   自您上一次執行此命令後，端點值便未發生變更。此處再次執行命令是為了輕鬆找出資料端點值並貼入本教學課程中使用的組態檔。

   **describe-endpoint** 命令會傳回類似以下的回應。記錄 `endpointAddress` 值，供之後使用。

   ```
   {
   "endpointAddress": "a3qjEXAMPLEffp-ats.iot.us-west-2.amazonaws.com"
   }
   ```

1. 使用裝置的唯一名稱來取代 *uniqueThingName*。如果想要使用多個裝置執行本教學課程，請賦予每個裝置自己的名稱。例如 **TestDevice01**、**TestDevice02** 等等。

   輸入此命令來為您的 Raspberry Pi 建立新的 AWS IoT 物件資源。

   ```
   aws iot create-thing --thing-name "uniqueThingName"
   ```

   由於 AWS IoT 物件資源是雲端中裝置的*虛擬*表示法，因此我們可以在 中建立多個物件資源 AWS IoT ，以用於不同的用途。這些資源都可以由同一個實體 IoT 裝置使用，用以代表裝置的不同面向。
**注意**  
當您想要保護多個裝置的政策時，可以使用 `${iot:Thing.ThingName}` 來取代靜態物件名稱 `uniqueThingName`。

   這些教學課程每次只會使用一個物件資源。如此一來，在這些教學課程中，它們代表不同的示範，因此在您為示範建立 AWS IoT 資源之後，您可以使用您專門為每個示範建立的資源來返回並重複示範。

   如果您的 AWS IoT 物件資源已建立，命令會傳回類似這樣的回應。記錄 `thingArn` 值以供稍後在此裝置上建立要執行的任務時使用。

   ```
   {
   "thingName": "uniqueThingName",
   "thingArn": "arn:aws:iot:us-west-2:57EXAMPLE833:thing/uniqueThingName",
   "thingId": "8ea78707-32c3-4f8a-9232-14bEXAMPLEfd"
   }
   ```

1. 在終端機視窗中：

   1. 開啟文字編輯器，例如 `nano`。

   1. 複製此 JSON 文件並將其貼入開啟的文字編輯器中。  
****  

      ```
      {
          "Version":"2012-10-17",		 	 	 
          "Statement": [
              {
                  "Effect": "Allow",
                  "Action": [
                      "iot:Connect"
                  ],
                  "Resource": [
                      "arn:aws:iot:us-west-2:123456789012:client/uniqueThingName"
                  ]
              },
              {
                  "Effect": "Allow",
                  "Action": [
                      "iot:Publish"
                  ],
                  "Resource": [
                      "arn:aws:iot:us-west-2:123456789012:topic/test/dc/pubtopic",
                      "arn:aws:iot:us-west-2:123456789012:topic/$aws/events/job/*",
                      "arn:aws:iot:us-west-2:123456789012:topic/$aws/events/jobExecution/*",
                      "arn:aws:iot:us-west-2:123456789012:topic/$aws/things/uniqueThingName/jobs/*"
                  ]
              },
              {
                  "Effect": "Allow",
                  "Action": [
                      "iot:Subscribe"
                  ],
                  "Resource": [
                      "arn:aws:iot:us-west-2:123456789012:topicfilter/test/dc/subtopic",
                      "arn:aws:iot:us-west-2:123456789012:topic/$aws/events/jobExecution/*",
                      "arn:aws:iot:us-west-2:123456789012:topicfilter/$aws/things/uniqueThingName/jobs/*"
                  ]
              },
              {
                  "Effect": "Allow",
                  "Action": [
                      "iot:Receive"
                  ],
                  "Resource": [
                      "arn:aws:iot:us-west-2:123456789012:topic/test/dc/subtopic",
                      "arn:aws:iot:us-west-2:123456789012:topic/$aws/things/uniqueThingName/jobs/*"
                  ]
              },
              {
                  "Effect": "Allow",
                  "Action": [
                      "iot:DescribeJobExecution",
                      "iot:GetPendingJobExecutions",
                      "iot:StartNextPendingJobExecution",
                      "iot:UpdateJobExecution"
                  ],
                  "Resource": [
                      "arn:aws:iot:us-west-2:123456789012:topic/$aws/things/uniqueThingName"
                  ]
              }
          ]
      }
      ```

   1. 在編輯器的每個政策陳述式 `Resource` 區段中，使用 AWS 區域來取代 *us-west-2:57EXAMPLE833*、一個冒號字元 (:) 和 12 位數的 AWS 帳戶 編號。

   1. 在編輯器的在每個政策陳述式中，使用您賦予此物件資源的物件名稱來取代 *uniqueThingName*。

   1. 將文字編輯器中的檔案儲存為 **\$1/policies/jobs\$1test\$1thing\$1policy.json**。

      如果為多個裝置執行此程序，請在每個裝置上將檔案儲存為此檔案名稱。

1. 將 *uniqueThingName* 取代為裝置的物件名稱，然後執行此命令來建立為該裝置量身打造 AWS IoT 的政策。

   ```
   aws iot create-policy \
   --policy-name "JobTestPolicyForuniqueThingName" \
   --policy-document "file://~/policies/jobs_test_thing_policy.json"
   ```

   如果建立此政策，此命令會傳回類似以下的回應。  
****  

   ```
   {
       "policyName": "JobTestPolicyForuniqueThingName",
       "policyArn": "arn:aws:iot:us-west-2:57EXAMPLE833:policy/JobTestPolicyForuniqueThingName",
       "policyDocument": "{\n\"Version\": \"2012-10-17\",\n\"Statement\": [\n{\n\"Effect\": \"Allow\",\n\"Action\": [\n\"iot:Connect\"\n],\n\"Resource\": [\n\"arn:aws:iot:us-west-2:57EXAMPLE833:client/PubSubTestThing\"\n]\n},\n{\n\"Effect\": \"Allow\",\n\"Action\": [\n\"iot:Publish\"\n],\n\"Resource\": [\n\"arn:aws:iot:us-west-2:57EXAMPLE833:topic/test/dc/pubtopic\"\n]\n},\n{\n\"Effect\": \"Allow\",\n\"Action\": [\n\"iot:Subscribe\"\n],\n\"Resource\": [\n\"arn:aws:iot:us-west-2:57EXAMPLE833:topicfilter/test/dc/subtopic\"\n]\n},\n{\n\"Effect\": \"Allow\",\n\"Action\": [\n\"iot:Receive\"\n],\n\"Resource\": [\n\"arn:aws:iot:us-west-2:57EXAMPLE833:topic/test/dc/*\"\n]\n}\n]\n}\n",
       "policyVersionId": "1"
   }
   ```

1. 使用裝置的物件名稱來取代 *uniqueThingName*，再使用稍早您在本節為此裝置儲存的 `certificateArn` 值來取代 `certificateArn`，然後執行此命令來將政策連接至裝置憑證。

   ```
   aws iot attach-policy \
   --policy-name "JobTestPolicyForuniqueThingName" \
   --target "certificateArn"
   ```

   若成功，此命令不會傳回任何內容。

1.  將 *uniqueThingName* 取代為裝置的物件名稱，將 取代`certificateArn`為您稍早在本節中儲存`certificateArn`的值，然後執行此命令以將裝置憑證連接至 AWS IoT 物件資源。

   ```
   aws iot attach-thing-principal \
   --thing-name "uniqueThingName" \
   --principal "certificateArn"
   ```

   若成功，此命令不會傳回任何內容。

在成功佈建 Raspberry Pi 之後，您就可以在測試中為另一 Raspberry Pi 重複本節程序；或者，如果所有裝置都已佈建，則繼續進行 [設定 AWS IoT 裝置用戶端以執行任務代理程式](#iot-dc-runjobs-prepare-config)。

## 設定 AWS IoT 裝置用戶端以執行任務代理程式
<a name="iot-dc-runjobs-prepare-config"></a>

此程序會為 AWS IoT 裝置用戶端建立組態檔案，以執行任務代理程式：。

注意：如果要準備一個以上的裝置，則必須在每個裝置上執行此程序。

**若要建立組態檔案以測試 AWS IoT 裝置用戶端：**

1. 在連接至 Raspberry Pi 本機主機電腦上的終端機視窗中：

   1. 開啟文字編輯器，例如 `nano`。

   1. 複製此 JSON 文件並將其貼入開啟的文字編輯器中。

      ```
      {
        "endpoint": "a3qEXAMPLEaffp-ats.iot.us-west-2.amazonaws.com",
        "cert": "~/certs/jobs/device.pem.crt",
        "key": "~/certs/jobs/private.pem.key",
        "root-ca": "~/certs/AmazonRootCA1.pem",
        "thing-name": "uniqueThingName",
        "logging": {
          "enable-sdk-logging": true,
          "level": "DEBUG",
          "type": "STDOUT",
          "file": ""
        },
        "jobs": {
          "enabled": true,
          "handler-directory": ""
        },
        "tunneling": {
          "enabled": false
        },
        "device-defender": {
          "enabled": false,
          "interval": 300
        },
        "fleet-provisioning": {
          "enabled": false,
          "template-name": "",
          "template-parameters": "",
          "csr-file": "",
          "device-key": ""
        },
        "samples": {
          "pub-sub": {
            "enabled": false,
            "publish-topic": "",
            "publish-file": "",
            "subscribe-topic": "",
            "subscribe-file": ""
          }
        },
        "config-shadow": {
          "enabled": false
        },
        "sample-shadow": {
          "enabled": false,
          "shadow-name": "",
          "shadow-input-file": "",
          "shadow-output-file": ""
        }
      }
      ```

   1. 將*端點*值取代為您 AWS 帳戶 在 中找到之 的裝置資料端點值[在 中佈建您的裝置 AWS IoT Core](iot-dc-install-provision.md#iot-dc-install-dc-provision)。

   1. 用此裝置使用的物件名稱取代 *uniqueThingName*。

   1. 將文字編輯器中的檔案儲存為 **\$1/dc-configs/dc-jobs-config.json**。

1. 執行此命令來設定新組態檔的檔案許可。

   ```
   chmod 644 ~/dc-configs/dc-jobs-config.json
   ```

您不必使用 **MQTT test client** (MQTT 測試用戶端) 進行此測試。雖然裝置將與 交換任務相關的 MQTT 訊息 AWS IoT，但任務進度訊息只會與執行任務的裝置交換。由於任務進度訊息只會與執行任務的裝置交換，因此您無法從其他裝置訂閱這些訊息，例如 AWS IoT 主控台。

儲存組態檔之後，您就可以繼續進行 [AWS IoT 使用 AWS IoT 裝置用戶端在 中建立和執行任務](iot-dc-runjobs-prepare-define.md)。

# AWS IoT 使用 AWS IoT 裝置用戶端在 中建立和執行任務
<a name="iot-dc-runjobs-prepare-define"></a>

本節中的程序會建立任務文件和 AWS IoT 任務資源。建立任務資源後， 會將任務文件 AWS IoT 傳送至任務代理程式將任務文件套用至裝置或用戶端的指定任務目標。

**Topics**
+ [建立和存放 IoT 任務的任務文件](#iot-dc-runjobs-prepare-define-jobdoc)
+ [AWS IoT 在一個 IoT 裝置的 中執行任務](#iot-dc-runjobs-prepare-define-job)

## 建立和存放 IoT 任務的任務文件
<a name="iot-dc-runjobs-prepare-define-jobdoc"></a>

此程序會建立簡單的任務文件，以包含在 AWS IoT 任務資源中。此任務文件會在任務目標上 顯示 "Hello world\$1"。

**若要建立和儲存任務文件：**

1. 選取要儲存任務文件的 Amazon S3 儲存貯體。如果沒有可使用的現有 Amazon S3 儲存貯體，就需要建立一個。如需如何建立 Amazon S3 儲存貯體的詳細資訊，請參閱 [Amazon S3 入門](https://docs.aws.amazon.com//AmazonS3/latest/userguide/GetStartedWithS3.html)中的主題。

1. 建立並儲存此任務的任務文件

   1. 在本機主機電腦上，開啟文字編輯器。

   1. 將此文字複製並貼入編輯器中。

      ```
      {
          "operation": "echo",
          "args": ["Hello world!"]
      }
      ```

   1. 在本機主機電腦上，將編輯器的內容儲存至名為 **hello-world-job.json** 的檔案。

   1. 請確認檔案已正確儲存。某些文字編輯器會在儲存文字檔案時自動附加 `.txt` 至檔案名稱。如果編輯器附加了 `.txt` 至檔案名稱，請更正檔案名稱後再繼續。

1. 使用 **hello-world-job.json** 路徑來取代 *path\$1to\$1file*；如果該路徑不在當前目錄中，則使用所選儲存貯體的 Amazon S3 儲存貯體路徑取代 *s3\$1bucket\$1name*，然後執行此命令將任務文件放入 Amazon S3 儲存貯體中。

   ```
   aws s3api put-object \
   --key hello-world-job.json \
   --body path_to_file/hello-world-job.json --bucket s3_bucket_name
   ```

   識別儲存在 Amazon S3 中的任務文件的任務文件 URL 是透過取代以下 URL 中 *s3\$1bucket\$1name* 和 *AWS\$1region* 來決定。記錄產生的 URL 以便稍後使用為 *job\$1document\$1path*

   ```
   https://s3_bucket_name.s3.AWS_Region.amazonaws.com/hello-world-job.json
   ```
**注意**  
AWS 安全性可防止您在 外部開啟此 URL AWS 帳戶，例如使用瀏覽器。根據預設，具有 檔案存取權 AWS IoT 的任務引擎會使用 URL。在生產環境中，您需要確保 AWS IoT 服務具有存取儲存在 Amazon S3 中任務文件的許可。

在儲存任務文件的 URL 之後，請繼續進行 [AWS IoT 在一個 IoT 裝置的 中執行任務](#iot-dc-runjobs-prepare-define-job)。

## AWS IoT 在一個 IoT 裝置的 中執行任務
<a name="iot-dc-runjobs-prepare-define-job"></a>

本節中的程序會啟動 Raspberry Pi 上的 AWS IoT Device Client，以在裝置上執行任務代理程式，以等待任務執行。它也會在 中建立任務資源 AWS IoT，將任務傳送至 IoT 裝置並在其上執行。

**注意**  
此程序只會在單一裝置上執行任務。

**若要啟動 Raspberry Pi 任務代理程式：**

1. 在本機主機電腦上連線至 Raspberry Pi 的終端機視窗中，執行此命令來啟動 AWS IoT 裝置用戶端。

   ```
   cd ~/aws-iot-device-client/build
   ./aws-iot-device-client --config-file ~/dc-configs/dc-jobs-config.json
   ```

1. 在終端機視窗中，確認 AWS IoT 裝置用戶端並顯示這些訊息

   ```
   2021-11-15T18:45:56.708Z [INFO]  {Main.cpp}: Jobs is enabled
                         .
                         .
                         .
   2021-11-15T18:45:56.708Z [INFO]  {Main.cpp}: Client base has been notified that Jobs has started
   2021-11-15T18:45:56.708Z [INFO]  {JobsFeature.cpp}: Running Jobs!
   2021-11-15T18:45:56.708Z [DEBUG] {JobsFeature.cpp}: Attempting to subscribe to startNextPendingJobExecution accepted and rejected
   2021-11-15T18:45:56.708Z [DEBUG] {JobsFeature.cpp}: Attempting to subscribe to nextJobChanged events
   2021-11-15T18:45:56.708Z [DEBUG] {JobsFeature.cpp}: Attempting to subscribe to updateJobExecutionStatusAccepted for jobId +
   2021-11-15T18:45:56.738Z [DEBUG] {JobsFeature.cpp}: Ack received for SubscribeToUpdateJobExecutionAccepted with code {0}
   2021-11-15T18:45:56.739Z [DEBUG] {JobsFeature.cpp}: Attempting to subscribe to updateJobExecutionStatusRejected for jobId +
   2021-11-15T18:45:56.753Z [DEBUG] {JobsFeature.cpp}: Ack received for SubscribeToNextJobChanged with code {0}
   2021-11-15T18:45:56.760Z [DEBUG] {JobsFeature.cpp}: Ack received for SubscribeToStartNextJobRejected with code {0}
   2021-11-15T18:45:56.776Z [DEBUG] {JobsFeature.cpp}: Ack received for SubscribeToStartNextJobAccepted with code {0}
   2021-11-15T18:45:56.776Z [DEBUG] {JobsFeature.cpp}: Ack received for SubscribeToUpdateJobExecutionRejected with code {0}
   2021-11-15T18:45:56.777Z [DEBUG] {JobsFeature.cpp}: Publishing startNextPendingJobExecutionRequest
   2021-11-15T18:45:56.785Z [DEBUG] {JobsFeature.cpp}: Ack received for StartNextPendingJobPub with code {0}
   2021-11-15T18:45:56.785Z [INFO]  {JobsFeature.cpp}: No pending jobs are scheduled, waiting for the next incoming job
   ```

1. 在終端機視窗中看到此訊息之後，請繼續執行下一個程序並建立任務資源。請注意，它可能不是清單中的最後一個項目。

   ```
   2021-11-15T18:45:56.785Z [INFO]  {JobsFeature.cpp}: No pending jobs are scheduled, waiting for the next incoming job
   ```

**建立 AWS IoT 任務資源**

1. 在本機主機電腦上：

   1. 使用來自 [建立和存放 IoT 任務的任務文件](#iot-dc-runjobs-prepare-define-jobdoc) 的任務文件 URL 來取代 *job\$1document\$1url*。

   1. 使用為裝置建立的物件資源 ARN 來取代 *thing\$1arn*，然後執行此命令。

      ```
      aws iot create-job \
      --job-id hello-world-job-1 \
      --document-source "job_document_url" \
      --targets "thing_arn" \
      --target-selection SNAPSHOT
      ```

      如果成功，命令會傳回類似以下的結果。

      ```
      {
        "jobArn": "arn:aws:iot:us-west-2:57EXAMPLE833:job/hello-world-job-1",
        "jobId": "hello-world-job-1"
      }
      ```

1. 在終端機視窗中，您應該會看到來自 AWS IoT Device Client 的輸出，如下所示。

   ```
   2021-11-15T18:02:26.688Z [INFO]  {JobsFeature.cpp}: No pending jobs are scheduled, waiting for the next incoming job
   2021-11-15T18:10:24.890Z [DEBUG] {JobsFeature.cpp}: Job ids differ
   2021-11-15T18:10:24.890Z [INFO]  {JobsFeature.cpp}: Executing job: hello-world-job-1
   2021-11-15T18:10:24.890Z [DEBUG] {JobsFeature.cpp}: Attempting to update job execution status!
   2021-11-15T18:10:24.890Z [DEBUG] {JobsFeature.cpp}: Not including stdout with the status details
   2021-11-15T18:10:24.890Z [DEBUG] {JobsFeature.cpp}: Not including stderr with the status details
   2021-11-15T18:10:24.890Z [DEBUG] {JobsFeature.cpp}: Assuming executable is in PATH
   2021-11-15T18:10:24.890Z [INFO]  {JobsFeature.cpp}: About to execute: echo Hello world!
   2021-11-15T18:10:24.890Z [DEBUG] {Retry.cpp}: Retryable function starting, it will retry until success
   2021-11-15T18:10:24.890Z [DEBUG] {JobsFeature.cpp}: Created EphermalPromise for ClientToken 3TEWba9Xj6 in the updateJobExecution promises map
   2021-11-15T18:10:24.890Z [DEBUG] {JobEngine.cpp}: Child process now running
   2021-11-15T18:10:24.890Z [DEBUG] {JobEngine.cpp}: Child process about to call execvp
   2021-11-15T18:10:24.890Z [DEBUG] {JobEngine.cpp}: Parent process now running, child PID is 16737
   2021-11-15T18:10:24.891Z [DEBUG] {16737}: Hello world!
   2021-11-15T18:10:24.891Z [DEBUG] {JobEngine.cpp}: JobEngine finished waiting for child process, returning 0
   2021-11-15T18:10:24.891Z [INFO]  {JobsFeature.cpp}: Job exited with status: 0
   2021-11-15T18:10:24.891Z [INFO]  {JobsFeature.cpp}: Job executed successfully!
   2021-11-15T18:10:24.891Z [DEBUG] {JobsFeature.cpp}: Attempting to update job execution status!
   2021-11-15T18:10:24.891Z [DEBUG] {JobsFeature.cpp}: Not including stdout with the status details
   2021-11-15T18:10:24.891Z [DEBUG] {JobsFeature.cpp}: Not including stderr with the status details
   2021-11-15T18:10:24.892Z [DEBUG] {Retry.cpp}: Retryable function starting, it will retry until success
   2021-11-15T18:10:24.892Z [DEBUG] {JobsFeature.cpp}: Created EphermalPromise for ClientToken GmQ0HTzWGg in the updateJobExecution promises map
   2021-11-15T18:10:24.905Z [DEBUG] {JobsFeature.cpp}: Ack received for PublishUpdateJobExecutionStatus with code {0}
   2021-11-15T18:10:24.905Z [DEBUG] {JobsFeature.cpp}: Removing ClientToken 3TEWba9Xj6 from the updateJobExecution promises map
   2021-11-15T18:10:24.905Z [DEBUG] {JobsFeature.cpp}: Success response after UpdateJobExecution for job hello-world-job-1
   2021-11-15T18:10:24.917Z [DEBUG] {JobsFeature.cpp}: Ack received for PublishUpdateJobExecutionStatus with code {0}
   2021-11-15T18:10:24.918Z [DEBUG] {JobsFeature.cpp}: Removing ClientToken GmQ0HTzWGg from the updateJobExecution promises map
   2021-11-15T18:10:24.918Z [DEBUG] {JobsFeature.cpp}: Success response after UpdateJobExecution for job hello-world-job-1
   2021-11-15T18:10:25.861Z [INFO]  {JobsFeature.cpp}: No pending jobs are scheduled, waiting for the next incoming job
   ```

1. 當 AWS IoT 裝置用戶端正在執行並等待任務時，您可以透過變更 `job-id`值，並從**create-job**步驟 1 重新執行 來提交另一個任務。

當您完成執行任務時，請在終端機視窗中輸入 ^C(control-C) 以停止 AWS IoT 裝置用戶端。