ByteScope

input text

會改變裝置

把一段字串打進目前取得焦點的輸入欄位。

指令
adb shell input text
指令
輸入與觸控
風險
會改變裝置

語法

input text <string>

範例

  • input text 'hello world'

    記得加引號。單引號裡面 shell 什麼都不會動。

  • input text 'user@example.com'

    用真正的鍵盤而不是手機鍵盤來填登入欄位。

  • input keyevent 66

    之後補一個 Enter,因為 `input text` 不會打出換行。

輸出怎麼讀

什麼都沒有。字會出現在手機上,或不會出現——兩種都沒有回報,所以請看著螢幕。

輸出欄位

欄位意義
(no output)指令執行了。字有沒有進去,只有手機螢幕看得出來。

需要什麼權限

只要一條已授權的 `adb` 連線就夠了——不用 root、不用 `adb root`、不用 `su`,開發者選項裡除了 USB 偵錯本身也沒有東西要打開。`input` 是一行的 shell 腳本,跑的是 `cmd input "$@"`(AOSP 的 `cmds/input/input.sh`),所以字元是以 `shell` 使用者的身分注入的,而撐在後面的平台權限是 `android.permission.INJECT_EVENTS`,`com.android.shell` 在自己的資訊清單裡就有。這個授權刻意沒有給你的,是「瞄準」的能力。`runText` 只接受一個輸入來源、一個可選的 `-d <displayId>` 和那個字串,沒有別的——沒有任何參數能指名目標 app、activity 或欄位——所以字元會落在那個顯示器上、那一瞬間剛好有焦點的地方。因此在腳本裡,節奏與順序遠比存取權重要:一個在你沒看著的時候跳出來的對話框,是一個完全合法、可以讓你的密碼被打進去的地方。而且沒有任何確認可以等。`sendText` 把每個事件交給注入器之後就不再看結果,所以指令結束時並不知道下游有沒有收下任何一次按鍵;唯一誠實的確認是 `screencap`。

失敗訊息對照

訊息意義
java.lang.IllegalArgumentException: Argument expected after "text"`runText` 要一個必填參數而它沒拿到,所以 `input text` 後面什麼都不接就會丟例外。這一行會出現在 `Exception occurred while executing 'text':` 這段標題底下,帶著堆疊,全部走 stderr。要注意它「不是」什麼:它不是空字串。`input text ''` 是一個合法、有加引號的空參數,它會安靜地什麼都不做。
Exception occurred while executing 'text':shell 指令處理器接到一個 `Throwable`,標題的下一行會說出它是誰。非 ASCII 的情況就結束在這裡:`KeyCharacterMap.getEvents` 的文件寫明,當目前的按鍵字元對應表產不出那串字元時它會回傳 null,而這一頁查過的原始碼直接走訪 `events.length`、沒有檢查 null,所以含 `é` 或中日文的字串會丟 `NullPointerException`,而不是打出其中一部分。有回報說舊版失敗得更安靜——見下面的注意事項——這正是這一頁寧可一開始就拒絕非 ASCII,也不去試的原因。
Error: Invalid arguments for display ID.`-d` 拿到的東西不是整數,也不是 `DEFAULT_DISPLAY` 或 `INVALID_DISPLAY`。它是在解析階段丟出來的,在送出任何一個字元之前,所以什麼都沒打。要看的是 `-d` 後面那個 token,不是你的字串。
cmd: Can't find service: input`cmd` 找不到 `input` 服務可以把子指令交出去,因為 `input.sh` 只是 `cmd input` 的包裝。裝置還在開機、`system_server` 還沒登記它的時候,這是預期中的——和 `settings` 與 `window` 服務在同一時刻給的失敗長得一樣。

會改變什麼,以及怎麼回頭

裝置上不會有東西被刪掉,但打出去的字會送給當下取得焦點的任何東西:聊天訊息、搜尋框、登入或密碼欄位,或是你上次看螢幕之後才跳出來的確認對話框。一旦被送出就收不回來。還沒送出的話,`input keyevent 67`(Delete)可以一個字一個字刪掉;送出表單的是 `input keyevent 66`(Enter)。輸入前先看一眼螢幕,也不要把你不想留在欄位裡的機密打進去。

版本與廠商注意事項

  • 只吃 ASCII,而這是平台指令的限制,不是纜線或 shell 的限制:`input text` 最後會走到一張只有可列印 ASCII 按鍵的虛擬鍵盤字元對應表,所以中文、日文與帶重音的字母根本不會產生任何按鍵事件。桌面版 adb 一樣打不出來。
  • 參數裡的 `%s` 會被 `input` 自己在鍵盤看到它之前變成一個空白,而且沒有跳脫方式——`%%s` 會變成一個百分號加一個空白。請把字串切開,讓 `%` 與 `s` 不會在同一次呼叫裡相鄰。
  • 單引號帶不了單引號本身。請在那裡把字串切開,用 `input keyevent 75` 送出那個單引號;`'\''` 那種花招正是那種寫對一次、之後被抄錯的東西。
  • 參數加了引號之後空白不需要 `%s`:shell 會把空白保留在同一個詞裡,鍵盤再把它對應到空白鍵。

這個我們有 UI

Android ADB Toolbox 會透過 USB 執行這個指令並解析回應——只要 Chromium 系瀏覽器,不必安裝 SDK,也不會上傳任何東西。

開啟 Android ADB Toolbox

相關指令

和 input text 會在同一次作業裡出現的指令。

指令做什麼
input keyevent按下實體或虛擬按鍵——Home、Back、電源、音量、Enter。
getevent直接從核心串流原始輸入事件,或列出每個輸入裝置能回報什麼。
am start用元件或 intent 啟動 Activity——這是驗證 deep link 能不能被接走最快的方法。

這些行為是在撰寫 Web ADB 工具背後的解析程式時,對照 AOSP、toybox 與核心原始碼查證的。真的會因 Android 版本或廠牌而異的地方,這一頁會直接說明,而不是給你一個在半數手機上都是錯的數字。

常見問題

adb shell 指令要怎麼執行?

在手機上打開開發者選項與 USB 除錯、接上線,然後從裝了 platform-tools 的機器執行 adb shell <指令>——或者只執行 adb shell 取得互動提示字元,再直接打指令、不用前綴。如果指令會輸出二進位資料(例如截圖),請改用 adb exec-out 而不是 adb shell:單純的 shell 服務會配一個終端機,而它會改寫換行位元組並毀掉資料。

adb shell 指令需要 root 嗎?

這份參考裡的都不用。它們以 shell 使用者身分執行,可以針對自己的 Android 使用者管理套件、讀取大多數 dumpsys 服務、寫入設定提供者、注入輸入,以及讀取共用儲存空間。需要 root 的是這份參考刻意避開的事:讀取其他 app 的私有資料(除了對可除錯版本使用 run-as)、寫入系統分割區,或直接讀 /data/anr。

為什麼在我的手機上輸出看起來不一樣?

不是 Android 版本就是廠商。有些旗標與欄位是從特定版本才有的,那些都在各指令頁註明了。其餘是廠商改動,而受影響最深的是 dumpsys:一份 dump 是除錯輸出,它的文字就是那個服務的作者最後印了什麼,而廠商既會改它也會把改動往回移植。請用名稱而不是位置來讀欄位,並把沒見過的格式當成差異而不是失敗。

可以用瀏覽器而不是終端機來跑嗎?

很多指令可以——本站的 Web ADB 工具從 Chromium 系桌面瀏覽器透過 WebUSB 和手機對話,不必安裝 SDK,也不會上傳任何東西。每一頁在有對應工具時都會連過去。有兩個限制值得知道:同一時間只有一個程式能持有 ADB 介面,所以桌面的 adb server 要先停掉;而 adb forward 與 adb reverse 在瀏覽器裡完全無法運作。

比較想按而不是打字?Web ADB Toolkit 會從瀏覽器透過 USB 執行這類指令。或者瀏覽全部指令。