First, sorry if my question is obscure or in an inconvenient format. This is my first post here :D.
My issue is that I have a script, let's say test.sh which reads an input, and validates if it's a positive integer (reg ex used from this post:
BASH: Test whether string is valid as an integer?):
#!/bin/sh
echo -n " enter number <"
read num
if [[ $num =~ ^-?[0-9]+$ ]] #if num contains any symbols/letters
then # anywhere in the string
echo "not a positive int"
exit
else
echo "positive int read"
fi
I am running this script on my android device (Xiaomi Mi3 w) using adb shell and the error:
syntax error: =~ unexpected operator keeps displaying.
First, is my regex even correct?
Second, any hints on how I can overcome this syntax error?
The default shell in Android is mksh. It is not 100% compatible with bash. So do not expect all bash recipes to work without changes.
For the description of features supported by mksh - read its manual page.
This is a GNU bash POSIX regular expression. In Korn Shell, you can use extglob regular expressions to the same effect:
if [[ $num = ?(-)+([0-9]) ]]; then
…
See the section “File name patterns” in the manpage for details.
I had to use ksh expression as shown below to get this to work.
case $num in
+([0-9])*(.)*([0-9]) )
# Variable positive integer
echo "positive integer"
;;
*)
# Not a positive integer
echo "NOPE"
exit
;;
esac
Related
Using adb shell to run commands on an android device, I get different results when running ls with or without a wildcard ( globbing, i.e * ).
When running ls without a wildcard, the last path is displayed properly. When running ls with a wildcard, the path is displayed with an : in the end of it for some reason. The actual file does not have a : in its path.
My issue is specifically with the last file: /data/data/com.kauf.wrapmyFaceFunphotoeditor/files/DV-com.com.kauf.wrapmyFaceFunphotoeditor-2020-05-17-17-44-30-DEBUG.txt:
it has an : in the end which isn't supposed to be there
Why does using a wildcard in ls add characters to the result path?
Edit, environment details: Windows 10 / Android 7, the code is running on sh. I've ran adb shell to get to this command prompt, and doing it in one line (i.e adb shell su -c ls ...) returns similar results, same for adb shell command ...; also clarified the question.
As described in Why you shouldn't parse the output of ls, ls's behavior is not always well-defined. It's generally safer to use NULs (if you don't have any control or knowledge of filenames) or newlines (if you have reason to be certain that filenames can't contain them) to directly delimit a list of values emitted by the shell. Consider, then:
# output is separated by NULs, which cannot possibly exist in filenames
printf '%s\0' /data/data/com.kauf.wrapmyfacefunphotoeditor/files/DV-*
...or...
# output is separated by newlines; beware of a file named DV-evil<newline>something-else
printf '%s\n' /data/data/com.kauf.wrapmyfacefunphotoeditor/files/DV-*
Note that if you're passing this through extra unescaping layers, it may be necessary to double up your backslashes -- if you see literal 0s or ns separating filenames in your output, that's evidence of same.
Note also that if no matching files exist, a glob will expand to itself, so you can get an output that contains only the literal string /data/data/com.kauf.wrapmyfacefunphotoeditor/files/DV-*; in bash this can be suppressed with shopt -s nullglob, but with /bin/sh (particularly the minimal busybox versions more likely to be available on Android) this may not be available. One way to work around this is with code similar to the following:
# set list of files into $1, $2, etc
set -- /data/data/com.kauf.wrapmyfacefunphotoeditor/files/DV-*
# exit immediately if $1 does not exist
if [ "$#" -le 1 ] && [ ! -e "$1" ]; then
exit
fi
# otherwise, print the list in our desired format
printf '%s\0' "$#"
This is really odd...
I can't get this test to result in true in my linux shell and I can't figure out why.
#!/bin/bash
a=$(adb shell getprop ro.product.brand)
adb shell getprop ro.product.brand
if [ "$a" == "Huawei" ]; then
echo "Success"
else
echo "Failed"
fi
The script just outputs:
Huawei
Failed
Whereas this script:
b=$(whoami)
whoami
if [ "$b" == "amo" ]; then
echo "Success"
else
echo "Failed"
fi
...outputs:
amo
Success
Can anyone help me understand that?
I already tried cutting away spaces or line breaks in $a by piping to cut or sed but I get the same result...
I suggest this as a way to remove leading/trailing whitespace :
# Trims $1
# If $2 supplied, assigns result to variable named $2
# If $2 not present, echoes the value to stdout
trim()
{
if
[[ $1 =~ ^[[:blank:]]*(.*[^[:blank:]])[[:blank:]]*$ ]]
then
local result="${BASH_REMATCH[1]}"
else
local result="$1"
fi
if
(( $# > 1 ))
then
printf -v "$2" %s "$result"
else
printf %s "$result"
fi
}
This function uses no external program, so has low overhead.
Maybe a quick explanation of the regular expression...
^[[:blank:]]*(.*[^[:blank:]])[[:blank:]]*$
It matches all leading and trailing whitespace (no surprise there)
In the middle, it matches any string of characters that ends with a non-blank and saves that as a sub-expression for access with BASH_REMATCH
If there were no "non-blank" character specified to end the middle portion, the greedy .* would eat everything up until the end of the string, including trailing blanks.
The .* is, on the other hand, certain to begin with a non-blank, because the greedy initial [[:blank:]]* will only stop when encountering a non-blank.
Depending on your need, you may also use [[:space:]] instead of [[:blank:]] (difference explained here : https://en.wikipedia.org/wiki/Regular_expression#Character_classes). Basically, [[:blank:]] matches tabs and spaces, and [[:space:]] also matches newlines, carriage returns, and a few more.
I'm trying to get a shell script to take values. What I currently have is:
#!/system/bin/sh
echo "Enter the page numbers"
read page_no_first;
read page_no_final;
#
echo $page_no_first
echo $page_no_final
The echos are simply there for debug, and the problem is that they display as blanks.
The terminal results are as such:
scriptname
Enter the page numbers
1
1
5
5
Verbatim. That is the echo commands simply produce two empty lines.
I've found a sort of work around, and another related problem:
#!/system/bin/sh
echo "Enter the page numbers"
read page_no_first -s
read page_no_final -s
#
echo $page_no_first
echo $page_no_final
This should give me the read without it repeating the input. Instead, what it gives me is:
scriptname
Enter the page numbers
1
1
: is readonlyriptname[3]: read: -s
3
3
: is readonlyriptname[3]: read: -s
1
3
Ironically, this successfully writes the numbers to the variables, however, it a) does not give me a silent read, and b) gives me some funny error. Googling it doesn't help, since it's too vague.
Any help?
This work as expected (at least as I think it should):
#! /system/bin/sh
echo "Enter the page numbers"
echo -n "First: "
read page_no_first
echo -n "Last: "
read page_no_last
echo "First: $page_no_first Last: $page_no_last"
on android 4.2
I have this script which works on my linux machine
#!/bin/sh
c=1
if [ $c == 1 ]
then
echo c is 1
else
echo c is 0
fi
But when I use this in android as follows:
#!/system/bin/sh
c=1
if [ $c == 1 ]
then
echo c is 1
else
echo c is 0
fi
It gives an error like:
[: not found
EDIT
Is there any other logic to check the value of $c, whether it is 1 or 0 ?
Android shell have problem with [] in if so is there any other way to check the value of c ?
andriod shell sh is actually a link to busybox, and it is invoked as
busybox sh
you need setup [ applets manually
busybox ln -s /your_original_sh_path/busybox [
if you don't know where busybox is put, try list the /system/bin/sh which you give
ls /system/bin/sh
busybox which busybox
generally [ is an alias for test,
in Linux machine test is at
/usr/bin/test
and
if [ $c == 1 ]
is evaluated as
if test "$c" = 1
BUT here in android there is no test
so if with [] will not work in any case...
i will cross compile test for android and check it....!!!
Android does not provide a full UNIX environment, it is not a UNIX operating system. It has some similarities, much like how Windows also has some similarities to UNIX. Some Android devices and ROMs try to provide more of a UNIX-like environment that others, but you cannot rely on most of the standard shell scripting tools being installed if you are thinking about cross-device compatibility.
So for example, if you look at your GNU/Linux system, you can see that test and [ are actually programs. Try this: ls -l /usr/bin/[. Most Android installs do not include test or [. That means that if you want to try to do actual programming with Android's minimal shell environment, you have to use lots of odd tricks. You can install busybox to get a full UNIX shell environment, or you can even build busybox into your app. I do that when I need to include shell scripts in an app (for example, Lil' Debi and Commotion MeshTether).
Here's an example of writing a killall in Android's /system/bin/sh environment: http://en.androidwiki.com/wiki/Android_Shell_tips_and_tricks You can also use the various parameter expansions to create some logic, you can see an example of that in the Barnacle Wifi Tether scripts.
Use bash:
#!/system/bin/bash
or
#!/system/xbin/bash
You can check where your sh binary is pointing to on your Linux machine:
ls -l /bin/sh
Edit
BTW, use:
c=1
if [ $c -eq 1 ]
then
echo c is 1
else
echo c is 0
fi
Think you using the wrong arithmetic operator and there is a syntax error of a missing ";": try
[ $c -eq 1 ];
Also your location for Bash (sh) might be wrong at the top of your file:
#!/system/bin/sh
How about checking that the .sh file doesn't contain a carriage return before line feed.
Windows \r\n -> CR LF
Unix \n -> LF
use /system/bin/cmp for equality test.
if you need numerically test, substitute $(($c == 1)) with $c
#!/system/bin/sh
echo $c >/tmp/a
echo 1 >/tmp/b
if cmp /tmp/a /tmp/b
echo c is 1
else
echo c is 0
fi
I run into this issue also and found a solution (on another site)
if [[ $b -gt 0]]
then
echo 'Hooray it works'
else
echo 'still works'
fi
Can you use ADB to type directly on an android device from a computer? If so, how?
Although this question is rather old, I'd like to add this answer:
You may use adb shell input keyevent KEYCODE resp. adb shell input text "mytext". A list of all keycodes can be found here
As Manuel said, you can use adb shell input text, but you need to replace spaces with %s, as well as handle quotes. Here's a simple bash script to make that very easy:
#!/bin/bash
text=$(printf '%s%%s' ${#}) # concatenate and replace spaces with %s
text=${text%%%s} # remove the trailing %s
text=${text//\'/\\\'} # escape single quotes
text=${text//\"/\\\"} # escape double quotes
# echo "[$text]" # debugging
adb shell input text "$text"
Save as, say, atext and make executable. Then you can invoke the script without quotes...
atext Hello world!
...unless you need to send quotes, in which case you do need to put them between the other type of quotes (this is a shell limitation):
atext "I'd" like it '"shaken, not stirred"'
To avoid expansion/evaluation of the text parameter (i.e. for special characters like '$' or ';'), you could wrap them into quotes like this:
adb shell "input text 'insert your text here'"
input does not support UTF-8 or other encodings, you will see something like this if you try it
$ adb shell input text ö
Killed
therefore if these are your intention you need something more robust.
The following script uses AndroidViewClient/culebra with CulebraTester2-public backend to avoid input limitations.
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
from com.dtmilano.android.viewclient import ViewClient
vc = ViewClient(*ViewClient.connectToDeviceOrExit(), useuiautomatorhelper=True)
oid = vc.uiAutomatorHelper.ui_device.find_object(clazz='android.widget.EditText').oid
vc.uiAutomatorHelper.ui_object2.set_text(oid, '你好世界 😄')
it finds an EditText and then enters some Chinese characters and a emoji.
You can achieve the same using bash and curl if entering text is the only you need.
#! /bin/bash
#
# simple-input-text
# - Finds an EditText
# - Enters text
#
# prerequisites:
# - adb finds and lists the device
# - ./culebratester2 start-server
# - jq installed (https://stedolan.github.io/jq/)
#
set -e
set +x
base_url=http://localhost:9987/v2/
do_curl() {
curl -sf -H "accept: application/json" -H "Content-Type: application/json" "$#"
}
oid=$(do_curl -X POST "${base_url}/uiDevice/findObject" \
-d "{'clazz': 'android.widget.EditText'}" | jq .oid)
do_curl -X POST "${base_url}/uiObject2/${oid}/setText" \
-d "{'text': '你好世界 😄'}"
Here is a Bash-based solution that works for arbitrary/complex strings (e.g. random passwords). The other solutions presented here all failed for me in that regard:
#!/usr/bin/env bash
read -r -p "Enter string: " string # prompt user to input string
string="${string// /%s}" # replace spaces in string with '%s'
printf -v string "%q" "${string}" # quote string in a way that allows it to be reused as shell input
adb shell input text "${string}" # input string on device via adb
The following code may be used for repeated/continuous input:
#!/usr/bin/env bash
echo
echo "Hit CTRL+D or CTRL+C to exit."
echo
while true; do
read -r -p "Enter string: " string || { echo "^D"; break; }
string="${string// /%s}"
printf -v string "%q" "${string}"
echo "Sending string via adb..."
adb shell input text "${string}"
done
You can see how it's done in talkmyphone.
They are using Jabber, but it might be useful for you.
When using zsh, here is a more robust function to feed text to Android:
function adbtext() {
while read -r line; do
adb shell input text ${(q)${line// /%s}}
done
}
While Zsh quoting may be slightly different from a regular POSIX shell, I didn't find anything it wouldn't work on. Dan's answer is missing for example > which also needs to be escaped.
I am using it with pass show ... | adbtext.