I'm trying to do a pipe operation on my Android device through adb (this is for an automated script).
The operation is to fetch the most recently modified file in a particular directory and then delete it.
Let us say this file is file.txt and it is in /sdcard/Android/data/my.app.package on the Android device.
When I try to do adb shell ls -t /sdcard/Android/data/my.app.package | head -1 | xargs rm -f it throws the error:
rm: file.txt: No such file or directory
This is because it expects the full path.
So then I tried ls -t /sdcard/Android/data/my.app.package | head -1 | xargs ls -d | xargs rm -f but it complains with the same error.
Perhaps I need to pass in the $PWD along with the file name to xargs. How can I do that, or is there a better way to do this?
Edit: I have now tried ls -t sdcard/Android/data/my.app.package | head -1 | xargs -I '{}' ls sdcard/Android/data/my.app.package/'{}' and while a similar command works correctly on the Linux system as expected, it does weird stuff on the Android device. Possibly some missing implementation of xargs on Android stack.
A command like
adb shell foo | bar
runs foo in adb shell, and has your local shell run bar and receive its input from adb shell. If bar should run in adb shell too, you want to pass the entire pipeline to adb shell:
adb shell 'foo | bar'
This part of your question is basically a duplicate of
How to type adb shell commands in one line?
Separately, your ls -t command is flawed in several ways. Generally, don't use ls in scripts; but the trivial fix is to run the command in that directory directly. Then you don't need to add the path back on:
adb shell 'cd /sdcard/Android/data/my.app.package &&
ls -t | head -1 | xargs rm -f'
This still suffers from the various vagaries of parsing ls output; probably a better solution is to use find instead. If you have the facilities of GNU find and related utilities available on the remote device, try
adb shell 'find /sdcard/Android/data/my.app.package -printf "%T# %p\\0" |
sort -r -z -n | head -z -n 1 | sed "s/^[^ ]* //" | xargs -0 rm'
(Adapted from https://stackoverflow.com/a/299911/874188 with quoting fixes to allow the overall command to be run inside single quotes.)
If adb shell does not provide access to GNU userspace tools, perhaps just make sure you have very detailed control over what files can land in your directory so that you can reasonably reliably parse the output from ls; or if you can't guarantee that, try hacking something in e.g. Perl.
It is unfortunate that there is no simple standard way to get the oldest or newest n files from a directory in a robust, machine-readable form.
It would be nice if there was a more succinct way than these arguably complex and slightly advanced tricks with find.
This is the solution that works:
adb shell ls /sdcard/Android/data/my.app.package/`adb shell ls -t /sdcard/Android/data/my.app.package | head -1` | adb shell xargs rm -f
Related
How can we pull multiple files with the same extension by using "abd" command?
More details, I know that we can use command
adb pull sdcard/folder target-folder
to get all file of the folder.
I use this command to filter file in the adb shell.
ls -lR sdcard/folder | grep "ext"
But I want to filter some files with the same extension and pull them.
So now, how can we combine two commands?
adb shell ls sdcard/folder/*.ext | tr '\r' ' ' | xargs -n1 adb pull
See adb pull multiple files
For Windows, even with gitbash installed (so find and xargs available), you have to use CMD for to iterate the file list.
for /f "delims=" %G in ('adb shell find sdcard/DCIM/Camera/20221111*') do adb pull -a "%G"
This will download all photos and videos matching the criteria(in my case, taken on the day 2022 Nov 11st).
Find more info googling for /f and linux find.
I am using the following command to copy the most recently added file from a connected device into the directory that I want:
adb pull sdcard/Robotium-Screenshots/filename.jpg D:\jenkins\jobs\
But it can copy only the file that I specify.
How can I copy the newest file from sdcard/Robotium-Screenshots/ to D:\jenkins\jobs\ without specifying it by name?
Here is a one-liner which would pull the last modified file from a specified folder:
adb exec-out "cd /sdcard/Robotium-Screenshots; toybox ls -1t *.jpg 2>/dev/null | head -n1 | xargs cat" > D:\jenkins\jobs\latest.jpg
Known limitations:
It relies on ls command to do the sorting based on modification time. Historically Android devices had multiple sources for the coreutils, namely toolbox and toybox multi-binaries. The toolbox version did not support timestamp based sorting. That basically means that this won't work on anything older than Android 6.0.
It uses adb exec-out command to make sure that binary output does not get mangled by the tty. Make sure to update your platform-tools to the latest version.
If you use a bash-like shell, you can do:
adb pull /sdcard/Robotium-Screenshots/`adb shell ls -t /sdcard/Robotium-Screenshots/ | head -1` ~/Downloads
You can get a bash-like shell through cygwin, msys, git for windows (based on msys). If you are on mac or linux, you already have a bash-like shell.
One way to do this would be to grab the file name using the following command:
adb shell ls -lt /sdcard/Robotium-Screenshots | head -n2 | tail -n+2 | awk '{print $8}'
I want to list all files and folders of music directory of android mobile. Where I am using
ls -l -R /sdcard/music
It gives all files and folders but not in a proper way.
Any one tell me adb shell command that displays files and folders hierarchy of a directory like "tree" linux command.
I don't know if you still looking for this, but I believe this command can help you (works on adb as well):
find . -print | sort | sed 's;[^/]*/;|---;g;s;---|; |;g'
It prints a directory tree (both folders and files) in a more readable way than
ls -l -R
If you want just a folder tree, you can use this one:
ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/'
You need to install busybox for android, it will add many well known linux tools to your phone.
You need a rooted phone for installing busybox.
Check it out here: https://play.google.com/store/apps/details?id=stericson.busybox&hl=fr
Is there a way of running adb commands on all connected devices? To uninstall an app from all connected devices with "adb uninstall com.example.android".
The commands I am interested in is mainly install and uninstall.
I was thinking about writing a bash script for this, but I feel like someone should have done it already :)
Create a bash file and name it e.g. adb+:
#!/bin/bash
adb devices | while read -r line
do
if [ ! "$line" = "" ] && [ "$(echo "$line" | awk '{print $2}')" = "device" ]
then
device=$(echo "$line" | awk '{print $1}')
echo "$device" "$#" ...
adb -s "$device" "$#"
fi
done
Usage: ./adb+ <command>
Building on #Oli's answer, this will also let the command(s) run in parallel, using xargs. Just add this to your .bashrc file:
function adball()
{
adb devices | egrep '\t(device|emulator)' | cut -f 1 | xargs -t -J% -n1 -P5 \
adb -s % "$#"
}
and apply it by opening a new shell terminal, . ~/.bashrc, or source ~/.bashrc.
If you only want to run on devices (or only on emulators), you can change the (device|emulator) grep by removing the one you don't want. This command as written above will run on all attached devices and emulators.
the -J% argument specifies that you want xargs to replace the first occurrence of % in the utility with the value from the left side of the pipe (stdin).
NOTE: this is for BSD (Darwin / Mac OS X) xargs. For GNU/Linux xargs, the option is -I%.
-t will cause xargs to print the command it is about to run immediately before running it.
-n1 means xargs should only use at most 1 argument in each invocation of the command (as opposed to some utilities which can take multiple arguments, like rm for example).
-P5 allows up to 5 parallel processes to run simultaneously. If you want instead to run the commands sequentially, simply remove the entire -P5 argument. This also allows you to have two variations of the command (adball and adbseq, for example) -- one that runs in parallel, the other sequentially.
To prove that it is parallel, you can run a shell command that includes a sleep in it, for example:
$ adball shell "getprop ro.serialno ; date ; sleep 1 ; date ; getprop ro.serialno"
You can use this to run any adb command you want (yes, even adball logcat will work! but it might look a little strange because both logs will be streaming to your console in parallel, so you won't be able to distinguish which device a given log line is coming from).
The benefit of this approach over #dtmilano's & approach is that xargs will continue to block the shell as long as at least one of the parallel processes is still running: that means you can break out of both commands by simply using ^C, just like you're used to doing. With dtmilano's approach, if you were to run adb+ logcat, then both logcat processes would be backgrounded, and so you would have to manually kill the logcat process yourself using ps and kill or pkill. Using xargs makes it look and feel just like a regular blocking command line, and if you only have one device, then it will work exactly like adb.
This is an improved version of the script from 強大な. The original version was not matching some devices.
DEVICES=`adb devices | grep -v devices | grep device | cut -f 1`
for device in $DEVICES; do
echo "$device $# ..."
adb -s $device $#
done
To add in the ~/.bashrc or ~/.zshrc:
alias adb-all="adb devices | awk 'NR>1{print \$1}' | parallel -rkj0 --tagstring 'on {}: ' adb -s {}"
Examples:
$ adb-all shell date
$ adb-all shell getprop net.hostname
$ adb-all sideload /path/to/rom.zip
$ adb-all install /path/filename.apk
$ adb-all push /usr/local/bin/frida-server-arm64 /data/local/tmp/frida-server
Explanation: awk extracts the device id/host (first column: print $1) of every lines except the first one (NR>1) to remove the "List of devices attached" header line), then gnu parallel runs adb -s <HOSTNAME> <whatever-is-passed-to-the-alias> on whatever non-empty line (-r) in the order specified (-k, to avoid random order / fastest response order) and prepend each line with on <DEVICE>:\t for clarity, all in parallel (-j0, possible to set another number to define how many adb should be ran in parallel instead of unlimited).
:)
This is the highest result on Google, so for all Windows users coming here let me add this solution by User zingh (slightly modified to accept arbitrary commands, rather than "only" install
Batch file (adball.bat):
FOR /F "skip=1" %%x IN ('adb devices') DO start adb -s %%x %*
Call as:
adball uninstall com.mypackage
(%* takes all input parameters, my line above makes it so that all commands are passed to adb as they are, so that you can type multiple words, flags etc.)
Note: you can even use this directly from the Android Studio "run all" popup, if you install the Powershell-plugin. You can add adball to your path, then double-tap ctrl and run
powershell adball uninstall com.mypackage
adb wrapper supports selecting multiple targets for adb commands and parallel execution.
From its README:
# Installation
./install.sh ~/apps/android-sdk-linux
# Execute adb commands on all connected devices.
adb set-target all
# Execute adb commands on given devices.
adb set-target emulator-5554 C59KGT14263422
# Use GNU parallel for parallel install.
adb set-parallel true
(Disclaimer: I have written half of it)
When I type:
adb devices
My output is (this can be variable, it can list 10 or 20 etc):
List of devices attached
0280414640c133d7 device
TA054085R1 device
Afterwards I'd like to run:
adb install MyApp 0280414640c133d7
adb install MyApp TA054085R1
How can I get this going in a bash script?
I'm not sure how robust you need your solution to be, but something like this will work with the case you describe above:
#!/bin/bash
echo "Deploying SONR to devices..."
#install SONR
for foo in `adb devices | egrep 'device$' | cut -d ' ' -f1`
do
adb -s $foo install SONR.apk
done
It is no doubt possible to replace the ugly egrep piped through cut with a single call to sed or awk or even a perl one-liner.