A single connected phone makes adb painless — every command implicitly targets it. The moment a second physical device, a second phone over Wi-Fi, or an emulator joins the picture, adb refuses to guess which one you mean and returns error: more than one device/emulator. This comes up constantly for anyone testing an app across several phones at once, or running an emulator alongside a physical test device.
adb devices -l
The -l flag adds extra detail (model name, USB transport ID) beyond the plain serial number, which helps when two devices have serials that are hard to tell apart at a glance. A typical output with a phone and an emulator running together looks like:
List of devices attached
R58N30ABCDE device usb:1-3 product:starqltesq model:SM_G960F
emulator-5554 device product:sdk_gphone64_x86_64
Once you have the serial number, insert -s <serial> right after adb and before the subcommand:
adb -s R58N30ABCDE shell getprop ro.product.model
adb -s emulator-5554 install app-debug.apk
This works for every adb subcommand — shell, install, logcat, push, pull, reboot — the flag simply scopes whichever command follows it to that one device. It's the most reliable option because it's explicit and doesn't depend on what type of device it is.
If you only ever have one physical device and one emulator connected at the same time, two shorter flags avoid typing out the serial:
adb -d shell — targets the single connected USB or network-attached physical device, and errors out if more than one is connected.adb -e shell — targets the single running emulator instance, and errors out if more than one emulator is running.These are convenient for scripts that always run against "whatever real phone is plugged in" without hardcoding a serial number that changes every time you swap test devices.
Typing -s <serial> on every single command gets old fast in a long shell session. Setting the ANDROID_SERIAL environment variable makes adb use that serial by default for the rest of the session:
# Linux/macOS
export ANDROID_SERIAL=R58N30ABCDE
# Windows PowerShell
$env:ANDROID_SERIAL="R58N30ABCDE"
Any explicit -s flag on an individual command still overrides the environment variable for that one call, which makes it easy to work primarily against one device while occasionally reaching over to check another.
A device connected via ADB over Wi-Fi shows up with an IP address and port as its serial instead of a hardware ID, for example 192.168.1.42:5555. The same -s flag works identically:
adb -s 192.168.1.42:5555 shell
This is particularly useful when a USB-connected phone and the same phone reconnected over Wi-Fi both appear in adb devices at once — without a serial, adb has no way to know which transport you meant.
To run the same command across every attached device rather than picking one, loop over the serials from adb devices:
for serial in $(adb devices | tail -n +2 | cut -f1); do
adb -s "$serial" shell getprop ro.product.model
done
This pattern is common in QA setups testing the same build across a rack of physical devices, since it avoids hardcoding any single serial number into the script.
Putting -s after the subcommand: adb shell -s R58N30ABCDE does not work the way you'd expect; -s has to come before the subcommand, immediately after adb.
Serial changes after a reboot into a different mode: a device's serial in normal ADB mode and its identifier in fastboot mode are usually the same string, but double-check with fastboot devices if a script that worked in ADB mode suddenly can't find the device after a reboot to the bootloader.
Stale entries after a bad disconnect: if a device shows up twice, or shows as offline, run adb kill-server followed by adb start-server to clear out stale connection state before trying to target it again.