When bash (or other shell) fails to guess the correct column count on your terminal, it gets inconvenient to edit long lines.
For usual Linux systems, there is script termsize that automatically fixes the terminal size. But typical Android does not have Python, so termsize can't work.
How do I fix the terminal width in adb shell?
This snippet can assist you for setting up column count:
echo "\033[18t"; { sleep 1; echo -n "COLUMNS="; } & grep -m 1 -o '[0-9]*t' | { sleep 2; grep -m 1 -o '[0-9]*'; }
Usage:
Open adb shell;
Insert the snippet into command line;
Execute it (press Enter) and immediately press Enter again.
Wait for 2 seconds: it should print COLUMNS= after 1 second and the columns count after another 1 second;
Copy COLUMNS=NNN into console and press Enter yet again.
Example session:
$ adb shell
9]*t' | { sleep 2; grep -m 1 -o '[0-9]*'; } <
[1] 17212
^[[8;38;149t
COLUMNS=149
[1] + Done { sleep 1 ; echo -n "COLUMNS=" ; }
shell#hwH60:/ $ COLUMNS=149
shell#hwH60:/ $
Related
im trying to create bash script to install split APKs manually with adb shell
that requires to get session id using the command bellow
command
SESSION='pm install-create -S 42211368'
this will output something like : Success: created install session [547376362]
547376362 will be the session ID
I want to pass 547376362 into SESSION Variable
sh < pm install-write -S 24628703 ${SESSION} 0 /sdcard/YTAPKM/base.apk
so result shall be "sh < pm install-write -S 24628703 547376362 0 /sdcard/YTAPKM/base.apk"
grep is sufficient for this.
SESSION=$(pm install-create -S 42211368 | grep -oE '[0-9]+')
sh < pm install-write -S 24628703 ${SESSION} 0 /sdcard/YTAPKM/base.apk
To explain what's happening a bit:
grep -E uses "extended" regular expressions (easier to work with)
grep -o outputs only the matching part, the integer in this case
SESSION=$(some_cmd) stores the stdout from some_cmd to the variable SESSION, and allows for pipes and such too
I'm developing an application that uses ADB Shell to interface with android devices, and I need some way of printing out the application name or label of an application, given maybe their package name.
In short, I need a way of getting app names (i.e. "Angry Birds v1.0.0") for user installed applications through adb shell.
Any light on the matter? Any help is appreciated on this.
adb shell pm list packages will give you a list of all installed package names.
You can then use dumpsys | grep -A18 "Package \[my.package\]" to grab the package information such as version identifiers etc
just enter the following command on command prompt after launching the app:
adb shell dumpsys window windows | find "mCurrentFocus"
if executing the command on linux terminal replace find by grep
If you know the app id of the package (like org.mozilla.firefox), it is easy.
First to get the path of actual package file of the appId,
$ adb shell pm list packages -f com.google.android.apps.inbox
package:/data/app/com.google.android.apps.inbox-1/base.apk=com.google.android.apps.inbox
Now you can do some grep|sed magic to extract the path : /data/app/com.google.android.apps.inbox-1/base.apk
After that aapt tool comes in handy :
$ adb shell aapt dump badging /data/app/com.google.android.apps.inbox-1/base.apk
...
application-label:'Inbox'
application-label-hi:'Inbox'
application-label-ru:'Inbox'
...
Again some grep magic to get the Label.
A shell script to accomplish this:
#!/bin/bash
# Remove whitespace
function remWS {
if [ -z "${1}" ]; then
cat | tr -d '[:space:]'
else
echo "${1}" | tr -d '[:space:]'
fi
}
for pkg in $(adb shell pm list packages -3 | cut -d':' -f2); do
apk_loc="$(adb shell pm path $(remWS $pkg) | cut -d':' -f2 | remWS)"
apk_name="$(adb shell aapt dump badging $apk_loc | pcregrep -o1 $'application-label:\'(.+)\'' | remWS)"
apk_info="$(adb shell aapt dump badging $apk_loc | pcregrep -o1 '\b(package: .+)')"
echo "$apk_name v$(echo $apk_info | pcregrep -io1 -e $'\\bversionName=\'(.+?)\'')"
done
Inorder to find an app's name (application label), you need to do the following:
(as shown in other answers)
Find the APK path of the app whose name you want to find.
Using aapt command, find the app label.
But devices don't ship with the aapt binary out-of-the-box.
So you will need to install it first. You can download it from here:
https://github.com/Calsign/APDE/tree/master/APDE/src/main/assets/aapt-binaries
Check this guide for complete steps:
How to find an app name using package name through ADB Android?
(Disclaimer: I am the author of that blog post)
This is what I just came up with. It gives a few errors but works well enough for my needs, matching package names to labels.
It pulls copies of all packages into subdirectories of $PWD, so keep that in mind if storage is a concern.
#!/bin/bash
TOOLS=~/Downloads/adt-bundle-linux-x86_64-20130717/sdk/build-tools/19.1.0
AAPT=$TOOLS/aapt
PMLIST=adb_shell_pm_list_packages_-f.txt
TEMP=$(echo $(adb shell mktemp -d -p /data/local/tmp) | sed 's/\r//')
mkdir -p packages
[ -f $PMLIST ] || eval $(echo $(basename $PMLIST) | tr '_' ' ') > $PMLIST
while read line; do
package=${line##*:}
apk=${package%%=*}
name=${package#*=}
copy=packages$apk
mkdir -p $(dirname $copy)
if [ ! -s $copy ]; then # copy it because `adb pull` doesn't see /mnt/expand/
adb shell cp -f $apk $TEMP/copy.apk
adb pull $TEMP/copy.apk $copy
fi
label=$($AAPT dump badging $copy || echo ERROR in $copy >&2 | \
sed -n 's/^application-label:\(.\)\(.*\)\1$/\2/p')
echo $name:$label
done < <(sed 's/\r//' $PMLIST)
adb shell rm -rf $TEMP
So I extremely grateful to jcomeau_ictx for providing the info on how to extract application-label info from apk and the idea to pull apk from phone directly!
However I had to make several alteration to script it self:
while read line; do done are breaking as a result of commands within while loop interacting with stdin/stdout and as a result while loop runs only once and then stops, as it is discussed in While loop stops reading after the first line in Bash - the comment from cmo I used solution provided and switched while loop to use unused file descriptor number 9.
All that the script really need is a package name and adb shell pm list packages -f is really excessive so I changed it to expect a file with packages list only and provided example on how one can get one from adb.
jcomeau_ictx script variant do not take in to account that some packages may have multiple apk associated with them which breaks the script.
And the least and last, I made every variable to start with underscore, it's just something that makes it easier to read script.
So here another variant of the same script:
#!/bin/bash
_TOOLS=/opt/android-sdk-update-manager/build-tools/29.0.3
_AAPT=${_TOOLS}/aapt
#adb shell pm list packages --user 0 | sed -e 's|^package:||' | sort >./packages_list.txt
_PMLIST=packages_list.txt
rm ./packages_list_with_names.txt
_TEMP=$(echo $(adb shell mktemp -d -p /data/local/tmp) | sed 's/\r//')
mkdir -p packages
[ -f ${_PMLIST} ] || eval $(echo $(basename ${_PMLIST}) | tr '_' ' ') > ${_PMLIST}
while read -u 9 _line; do
_package=${_line##*:}
_apkpath=$(adb shell pm path ${_package} | sed -e 's|^package:||' | head -n 1)
_apkfilename=$(basename "${_apkpath}")
adb shell cp -f ${_apkpath} ${_TEMP}/copy.apk
adb pull ${_TEMP}/copy.apk ./packages
_name=$(${_AAPT} dump badging ./packages/copy.apk | sed -n 's|^application-label:\(.\)\(.*\)\1$|\2|p' )
#'
echo "${_package} - ${_name}" >>./packages_list_with_names.txt
done 9< ${_PMLIST}
adb shell rm -rf $TEMP
I have set up an Android phone with the "SSH Server" app and would like to write a script to download the latest file in a particular directory using scp. The script is to be run from a Linux laptop
The problem is that the android doesn't contain commands like "head" or "tail" and I don't know how to select the latest file.
The best I can do is copy all the files from a directory with this:
#!/bin/bash
dst=username#192.168.1.107:storage/sdcard0/DCIM/Camera
scp -P 60839 -oHostKeyAlgorithms=+ssh-dss $dst/* /home/username/projects/3patetas
done 0
Can anyone help?
-------EDIT-------------
I thought the following might work but it causes the ssh server on the android to stop:
#!/bin/bash
remote_dir=/storage/sdcard0/DCIM/Camera
dst=username#192.168.1.107
scp -P 60839 -oHostKeyAlgorithms=+ssh-dss $dst:'ssh $dst cd $remote_dir ; latest="" ; for i in *.jpg ; do latest=$i ; done ; echo $latest' /home/username/projects/3patetas
exit 0
Does this help?
#!/bin/bash
remote_dir=/storage/sdcard0/DCIM/Camera
dst=username#192.168.1.107
name="$(ssh $dst "cd $remote_dir"' ; latest="" ; for i in *.jpg ; do latest=$i ; done ; echo $latest')"
scp -P 60839 -oHostKeyAlgorithms=+ssh-dss $dst:$remote_dir/$name /home/username/projects/3patetas
exit 0
Or this:
#!/bin/bash
remote_dir=/storage/sdcard0/DCIM/Camera
dst=username#192.168.1.107
name=($(ssh $dst "cd $remote_dir"' ; for i in *.jpg ; do date +%s -r $i ; echo $i ; done'))
name=$(printf "%s %s\n" ${name[#]} | sort -n | tail -1)
scp -P 60839 -oHostKeyAlgorithms=+ssh-dss $dst:$remote_dir/${name#* } /home/username/projects/3patetas
exit 0
I have a rather weird issue. I can't figure out why it's happening or how to fix it.
The script:
#!system/bin/sh
#set -x
reader() {
t2=-1
grep -v -E "add device|name:" | while IFS=' ' read -r t1 a b c d _; do
t1=${t1%%-*}
t=`expr $t1 - $t2`
if let 't > 0 && t2 > -1'; then
echo "sleep $t"
fi
printf 'sendevent %s' "${a%?}"
printf '%5d %5d %5d\n' "0x$b" "0x$c" "0x$d"
t2=$t1
done
}
let() {
IFS=, command eval [ '$(($*))' -ne 0 ]
}
countDown() {
echo 'Starting in...'
i=4
while [[ $i -gt 1 ]]; do
i=$(($i-1))
echo "$i"
sleep 1
done
printf '%s\n\n\n' 'Go!'
# echo "$*"
"$#" | reader
}
clear
printf '%s >' 'Catch by [n]umber of events or [t]imeout?'
read type
case $type in
n)
printf '%s >' 'Events to catch?'
read arg
echo "Gonna catch $arg events!"
countDown getevent -t -c "$arg"
;;
t)
printf '%s >' 'Timeout (in seconds)?'
read arg
echo "Gonna catch events for $arg seconds!"
countDown timeout -t $arg getevent -t
esac
The goal of the script:
Catch the user's interactions (e.g. key presses, screen taps, etc) using the getevent command and outputs a script to stdout, that can be used to replicate these events.
Additional info:
getevent's output is in hexadecimal format
sendevent accepts decimal format
Expected output:
Catch by [n]umber or by [t]imeout? n
Events to catch? > 4
Gonna catch 4 events...
Starting in...
3
2
1
Go!
sendevent /dev/input/event5 1 102 1
sendevent /dev/input/event5 0 0 0
sleep 3
sendevent /dev/input/event5 1 102 0
sendevent /dev/input/event5 0 0 0
The problem:
The code runs as expected when "n" is picked. When "t" is picked, the script exits after the specified amount of seconds. However, it does not output anything - the first line of the while loop doesn't even run.
With set -x, here's what's shown (last few lines):
+ printf %s\n\n\n Go!
Go!
+ reader
+ t2=-1
+ grep -v -E add device|name:
+ timeout -t 5 getevent -t
+ IFS = read -r t1 a b c d _
Running this alone shows output to stdout (output that's supposed to be read and modified inside the while loop):
timeout -t 5 getevent -t
Any ideas?
Thanks.
EDIT:
Alright, so I think it's a buffering issue. Basically this gives a similar issue:
getevent > output
The file gets updated every bunch of events, but not instantly (and might never update if not enough events are made). I'm not aware of any work around on Android.
I think there's no way of enabling line-buffering in busybox's grep, but you can do something like this:
$ adb shell timeout -t 10 getevent -t | \
grep --line-buffered -v -E "add device|name:" | \
while read line; do echo "READ: $line"; done
Here was basically that stuff I translated from your code before being mangled by bug workarounds. Replacing the one command I don't have with cat on the timeout branch, it runs correctly with your sample input in Busybox sh, dash, mksh, bash, and ksh93.
Since so many basic things about the shell and applications are broken, it isn't surprising this doesn't work. I would make sure Busybox is up-to-date and see whether the arithmetic and other bugs you keep hitting are reproducible on other systems, and report bugs if this code doesn't work.
#!/bin/sh
reader() {
t2=-1
grep -vE '^(add device|[[:space:]]+name:)' |
while IFS=' ' read -r t1 a b c d _; do
let "(t = (t1 = ${t1%%-*}) - t2) > 0 && t2 > -1" && echo "sleep $t"
printf 'sendevent %s' "${a%[[:digit:]]:}"
printf '%5d %5d %5d\n' "0x$b" "0x$c" "0x$d"
let t2=t1
done
}
let() {
IFS=, command eval test '$(($*))' -ne 0
}
countDown() {
echo 'Starting in...'
i=4
while let 'i-=1 > 0'; do
echo "$i"
sleep 1
done
printf '%s\n\n\n' 'Go!'
echo "$*"
"$#" <&3 | reader
}
isDigit() {
while ! ${1+false}; do
case $1 in
*[^[:digit:]]*|'') return 1
esac
command shift
done 2>/dev/null
}
main() {
printf '%s >' 'Catch by [n]umber of events or [t]imeout?'
read type
case $type in
n)
printf '%s >' 'Events to catch?'
read arg
isDigit "$arg" || return 1
echo "Gonna catch $arg events!"
countDown getevent -t -c "$arg"
;;
t)
printf '%s >' 'Timeout (in seconds)?'
read arg
isDigit "$arg" || return 1
echo "Gonna catch events for $arg seconds!"
countDown busybox timeout -t "$arg" cat -
;;
*)
return 1
esac
}
main "$#" 4<&0 <<\EOF 3<&0 <&4 4<&-
add device 1: /dev/input/event8
name: "bcm_headset"
add device 2: /dev/input/event7
name: "max8986_ponkey"
add device 3: /dev/input/event6
name: "sec_touchscreen"
add device 4: /dev/input/event5
name: "sec_keypad"
add device 5: /dev/input/event4
name: "orientation"
add device 6: /dev/input/event3
name: "accelerometer"
add device 7: /dev/input/event0
name: "proximity_sensor"
add device 8: /dev/input/event2
name: "geomagnetic_raw"
add device 9: /dev/input/event1
name: "geomagnetic"
45534-48646 /dev/input/event6: 0001 008b 00000001
45534-48646 /dev/input/event6: 0000 0000 00000000
45534-48646 /dev/input/event6: 0001 008b 00000000
45534-48646 /dev/input/event6: 0000 0000 00000000
EOF
# vim: set fenc=utf-8 ff=unix ft=sh :
Output:
$ bb --help
BusyBox v1.21.0 (2013-02-20 20:39:21 CST) multi-call binary.
Usage: bb [-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]
Unix shell interpreter
$ bb answers/countdown
Catch by [n]umber of events or [t]imeout? >t
Timeout (in seconds)? >5
Gonna catch events for 5 seconds!
Starting in...
3
2
1
Go!
busybox timeout -t 5 cat -
sendevent /dev/input/event6: 1 139 1
sendevent /dev/input/event6: 0 0 0
sendevent /dev/input/event6: 1 139 0
sendevent /dev/input/event6: 0 0 0
Had the same problem, and remembered a trick that I first used in MS-DOS 3.30, and later in Cygwin(not for timeout, but the more command is a magical bullet for lots of redirect problems):
timeout -t 8 getevent | more > getevent.txt
works well in Total Commander's shell
with su (not sh)
You're welcome :-)
It is really annoying if you adb push/pull large files to the device that there's no way to now how far along it is. Is it possible to run adb push or adb pull and get a progress bar using the 'bar' utility?
The main issue here is I think that adb expects two file names, if the input file could be replaced by stdin you could pipe through the 'bar' utility and get a progress bar. So far I haven't succeeded in doing so, but I'm not really a shell guru which is why I'm asking here :)
Note that I'm on Linux using bash.
It looks like the latest adb has progress support.
Android Debug Bridge version 1.0.32
device commands:
adb push [-p] <local> <remote>
- copy file/dir to device
('-p' to display the transfer progress)
However, the answers above also work for 'adb install' which do not have a progress option. I modified the first answer's script to work this way:
Create "adb-install.sh" somewhere in your PATH and run "adb-install.sh " instead of "adb install -f "
#!/bin/bash
# adb install with progressbar displayed
# usage: <adb-install.sh> <file.apk>
# original code from: http://stackoverflow.com/questions/6595374/adb-push-pull-with-progress-bar
function usage()
{
echo "$0 <apk to install>"
exit 1
}
function progressbar()
{
bar="================================================================================"
barlength=${#bar}
n=$(($1*barlength/100))
printf "\r[%-${barlength}s] %d%%" "${bar:0:n}" "$1"
# echo -ne "\b$1"
}
export -f progressbar
[[ $# < 1 ]] && usage
SRC=$1
[ ! -f $SRC ] && { \
echo "source file not found"; \
exit 2; \
}
which adb >/dev/null 2>&1 || { \
echo "adb doesn't exist in your path"; \
exit 3; \
}
SIZE=$(ls -l $SRC | awk '{print $5}')
export ADB_TRACE=all
adb install -r $SRC 2>&1 \
| sed -n '/DATA/p' \
| awk -v T=$SIZE 'BEGIN{FS="[=:]"}{t+=$7;system("progressbar " sprintf("%d\n", t/T*100))}'
export ADB_TRACE=
echo
echo 'press any key'
read n
Currently I have this little piece of bash:
function adb_push {
# NOTE: 65544 is the max size adb seems to transfer in one go
TOTALSIZE=$(ls -Rl "$1" | awk '{ sum += sprintf("%.0f\n", ($5 / 65544)+0.5) } END { print sum }')
exp=$(($TOTALSIZE * 7)) # 7 bytes for every line we print - not really accurate if there's a lot of small files :(
# start bar in the background
ADB_TRACE=adb adb push "$1" "$2" 2>&1 | unbuffer -p awk '/DATA/ { split($3,a,"="); print a[2] }' | unbuffer -p cut -d":" -s -f1 | unbuffer -p bar -of /dev/null -s $exp
echo # Add a newline after the progressbar.
}
It works somewhat, it shows a progress bar going from 0 to 100 which is nice. However, it won't be correct if you do a lot of small files, and worse, the bytes/s and total bytes shown by 'bar' aren't correct.
I challenge you to improve on my script; it shouldn't be hard! ;)
Here is my solution, it will show a simple progressbar and current numeric progress
[==================================================] 100%
Usage
./progress_adb.sh source destination
progress_adb.sh
#!/bin/bash
function usage()
{
echo "$0 source destination"
exit 1
}
function progressbar()
{
bar="=================================================="
barlength=${#bar}
n=$(($1*barlength/100))
printf "\r[%-${barlength}s] %d%%" "${bar:0:n}" "$1"
# echo -ne "\b$1"
}
export -f progressbar
[[ $# < 2 ]] && usage
SRC=$1
DST=$2
[ ! -f $SRC ] && { \
echo "source file not found"; \
exit 2; \
}
which adb >/dev/null 2>&1 || { \
echo "adb doesn't exist in your path"; \
exit 3; \
}
SIZE=$(ls -l $SRC | awk '{print $5}')
ADB_TRACE=adb adb push $SRC $DST 2>&1 \
| sed -n '/DATA/p' \
| awk -v T=$SIZE 'BEGIN{FS="[=:]"}{t+=$7;system("progressbar " sprintf("%d\n", t/T*100))}'
echo
Testing on Ubuntu 14.04
$ bash --version
GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)
TODO
directory support
progressbar size change when screen size change
Well I can give you an Idea:
ADB_TRACE=adb adb push <source> <destination>
returns logs for any command, so for example the copy command, which looks like:
writex: fd=3 len=65544: 4441544100000100000000021efd DATA....#....b..
here you can get the total bytes length before, with ls -a, then parse the output of adb with grep or awk, increment an interneral counter and send the current progress to the bar utility.
When you succeeded, please post the script here.