Have aliases in adb shell non-interactive mode - android

Hi I need to run things in the form of adb shell <command>
When I test everything out inside adb shell, it works because I was able to set some aliases in .bashrc.
However, when I do adb shell <command>, nothing works because .bashrc is not used when you run adb shell <command>, because it's in the non-interactive mode.
How can I work around this? Can I adb push some files to the filesystem so that the alias will be there when adb shell is run?

If your android device is rooted you can add your aliases for adb shell into the /system/etc/mkshrc file.

One way to do this is to issue several shell commands in a single ADB command. You can put them in a string, and separate them with semicolons, thus:
adb shell "alias foo bar ; export MY_VARIABLE=/path/to/somewhere ; my_executable "
The " are crucial here, make sure they are paired up correctly. You could run your .bashrc this way, thus:
adb shell "source /path/to/.bashrc ; my_executable"

You can write a bash script that sets the aliases and then executes your shell:
#!/usr/bin/bash
. $HOME/.bashrc
adb shell $#

Related

How do I run "adb shell" commands in a terminal emulator locally on an Android device?

From a shell on my PC, I can run adb shell cmd package list packages, and get a list of all installed packages. I would like to run this and similar commands locally on my Android phone (Nexus 6P) in a terminal emulator (currently using Termux).
If I open the same shell with /system/bin/sh, and then try to run /system/bin/cmd package list packages, nothing happens (no errors, just outputs nothing and reloads the prompt).
If I run /system/bin/cmd -l the list of options appears as expected. $PATH and $LD_LIBRARY_PATH are the same in both environments. One major difference is that echo $USER returns "shell" from adb shell, but returns my local username from /system/bin/sh launched from Termux.
Is there any way to replicate the behavior of commands run from adb shell in a terminal emulator locally on Android?
Edit:
My device is rooted, and I am OK with root only solutions.
The problem is Termux. By design, Termux runs only (or is mostly?) the Linux command line programs that you install from within Termux using apt or the newer "native" package management interface, e.g. apt install bsdtar. What you need to run the adb shell commands is a terminal emulator that can truly access the underlying Android file system, not just the Termux that is practically a chroot save for the fact that it's aware it's not running commands from the filesystem root /.
As a simple test, run the following command:
which ls
It should return something like /system/bin/ls. But if it returns something like /data/data/com.termux/files/usr/bin/applets/ls then you have to change your terminal emulator to something else. I suspect that Termux was designed to take into account the more restrictive shell execution policies that Google put into place after KitKat or the Android 4.X.
The Android distribution I'm using, LineageOS 14.1, comes with a built-in shell emulator that allows me to run commands found in /system/bin/ls.
I don't have a rooted Nougat device handy, but something like the following may be a close enough approximation to adb shell (assuming you are using SuperSU):
env -i USER=shell "$(PATH=/system/xbin:/system/bin:/su/bin:/sbin:/magisk/.core/bin which su)" shell --context u:r:shell:s0 --shell /system/bin/sh --command COMMAND
I (very briefly) tested it from Termux on a rooted Marshmallow device.
To elaborate:
the -i flag is used to start with an empty environment
USER=shell isn't specifically required, but for some reason su refuses to run with a completely empty environment
$(PATH=/system/xbin:/system/bin:/su/bin:/sbin:/magisk/.core/bin which su) points to the full path of the su binary on your device and can be hardcoded if you prefer
shell instructs the su binary to login as the shell user (the same as adb shell)
--context u:r:shell:s0 sets the appropriate SELinux context
--shell /system/bin/sh instructs SuperSU to use the system shell rather than it's own sush shell
Another option would be to actually run adb from the device, connecting to itself over TCP. If you need some functionality that is only available via adb (e.g. in my case it was adb forward) then this may be your only option. Unfortunately this isn't particularly convenient.
I wasn't able to find success with any publicly available adb binaries, so I build it myself with a few minor changes. You can see the sources I used and the changes I made at https://github.com/shakalaca/fastboot-adb-android and https://github.com/brbsix/fastboot-adb-android, respectively.
Once you have adb installed, here's an abbreviated list of commands I used to connect to the device:
# Add iptables rules to block external connections to port 9999'
su root iptables -N adbd
su root iptables -A adbd -i lo -p tcp -m tcp --dport 9999 -j ACCEPT
su root iptables -A adbd -p tcp -m tcp --dport 9999 -j DROP
su root iptables -A INPUT -j adbd
# Necessary in order to display authorization prompt
su shell setprop ro.debuggable 1
su shell setprop service.adb.tcp.port 9999
su root start adbd
adb connect 127.0.0.1:9999
adb wait-for-local-device
To shut down:
adb kill-server
su root stop adbd
su shell setprop ro.debuggable 0
su shell setprop service.adb.tcp.port 0
su root iptables -D INPUT -j adbd
su root iptables -F adbd
su root iptables -X adbd
So I tried this recently...if you're rooted you can use a terminal emulator.
su
then the command you want without "adb shell" part of it.
i tried the command "adb shell dumpsys deviceidle force-idle" in order to force device into doze.
I did this on the device via terminal emulator as:
"dumpsys deviceidle force-idle" and it did take effect.
also the dumpsys batterystats command worked.
be careful with commands with extensive text output, as the screen will be flooded with the output and will be unresponsive for some time.
EDIT
I originally answered this without the termux tag in mind. This worked for me while trying to execute shell commands on a vanilla emulator and saw this question while researching, so I tried to answer it differently.
You almost had it there in your question. You only need to execute sh:
int result = -1;
try {
final Process shell = Runtime.getRuntime().exec("sh");
final DataOutputStream commands = new DataOutputStream(shell.getOutputStream());
commands.writeBytes("write a series");
commands.writeBytes("of commands here");
commands.writeBytes("exit\n");
commands.flush();
result = shell.waitFor();
}
} catch (Exception e) {
e.printStackTrace();
}
If result == 0 the commands were succesful, else otherwise
Only rooted android
Busybox must be installed (though you can try without it)
Just write the normal command without the prefix adb

Copying files in ADB shell with run-as

Is there a way to write a script that will copy files from an ADB shell using run-as?
The only way I know of to copy in the adb shell is using cat source > dest (edit: modern android versions have the cp command, which makes this question unnecessary), but I am only able to quote the greater-than sign one level deep - so my script can pass it to adb shell, but not to adb shell run-as.
For example, this works:
adb shell "cat source > dest"
But this does not:
adb shell run-as "cat source > dest"
Nor this:
adb shell "run-as cat source \> dest"
I even tried created a small script and uploading it to the device, but I can't seem to run the script from the adb shell - it tells me "permission denied". I can't chmod the script, either.
The reason I want to do this is to copy a file into an app's private storage area - specifically, I am using a script to modify shared preferences and put the modified preferences back. Only the app itself or root can write to the file I want, however.
The use case in this scenario is coping a file to a protected location on the device, not retrieving it; for retrieving, there are already good answers in this question.
The OP tried to combine the following 3 commands (that he had no problem executing one after another in the interactive shell session) into a single non-interactive command:
adb shell
run-as com.example.app
cat /sdcard/temp_prefs.xml > shared_prefs/com.example.app_preferences.xml
For simplicity let's start from within an interactive adb shell session. If we just try to combine the last two commands into a single line:
run-as com.example.app cat /sdcard/temp_prefs.xml > shared_prefs/com.example.app_preferences.xml
This would not work because of how shell redirection works - only the cat /sdcard/temp_prefs.xml part of the command would be run with com.example.app UID
Many people "know" to put the part of the command around redirection into quotes:
run-as com.example.app "cat /sdcard/temp_prefs.xml > shared_prefs/com.example.app_preferences.xml"
This does not work because the run-as command is not smart enough to parse the whole command. It expects an executable as the next parameter. The proper way to do it would be to use sh instead:
run-as com.example.app sh -c "cat /sdcard/temp_prefs.xml > shared_prefs/com.example.app_preferences.xml"
So can we just prepend adb shell to the command and be done with it? Not necessarily. By running the command from your PC you also add another local shell and its parser. Specific escape requirements would depend on your OS. In Linux or OSX (if your command does not already contain any ') it is easy to single-quote the whole command like so:
adb shell 'run-as com.example.app sh -c "cat /sdcard/temp_prefs.xml > shared_prefs/com.example.app_preferences.xml"'
But sometimes it is just easier to use an alternative solutions with (-out or less) quotes:
adb shell run-as com.example.app cp /sdcard/temp_prefs.xml shared_prefs/com.example.app_preferences.xml
Or if your device does not have the cp command:
adb shell run-as com.example.app dd if=/sdcard/temp_prefs.xml of=shared_prefs/com.example.app_preferences.xml
Also notice how I used shared_prefs/com.example.app_preferences.xml instead of full /data/data/com.example.app/shared_prefs/com.example.app_preferences.xml - normally inside of run-as command your current directory is the HOME dir of your package.
Following Chris Stratton's advice, the way I eventually got this to work was as follows (for copying shared preferences back to the device):
adb push shared_prefs.xml /sdcard/temp_prefs.xml
cat <<EOF | adb shell
run-as com.example.app
cat /sdcard/temp_prefs.xml > /data/data/com.example.app/shared_prefs/com.example.app_preferences.xml
exit
exit
EOF
Piping directly to adb shell run-as did not work, and I do not know why, but piping to adb shell does. The trick is to then call run-as from the interactive shell, and it continues to accept input from the pipe.
The HERE doc lets me easily embed the newlines to separate commands and in general just makes it readable; I did not have much luck with semicolons, but that might have been because of the way I was doing things. I believe it might work with other methods of piping multiple commands/newlines; I stopped the experiment once I finally got it to work.
The two exits are necessary to prevent a hanging shell (killable with CTRL-C); one for run-as, and the other for adb shell itself. Adb's shell doesn't respond to end-of-file very nicely, it seems.
you could just change the permission of the directory and then pull all the files out. but for me i was looking for just one shared preference file and i was able to get the data like this:
PACKAGE='com.mypackage.cool'
SHAREDPREF_FILE="${PACKAGE}_preferences.xml"
adb shell "run-as $PACKAGE cat /data/data/$PACKAGE/shared_prefs/$SHAREDPREF_FILE">$SHAREDPREF_FILE
now we have the data of the sharedpreference file stored in a file of the same name.
Using the latest adb (ADB v1.0.41 / Version 33.0.3) and a Play Store emulator image I experienced adb root not being granted. I also could not copy from /data/local/ or /storage/emulated/0/ due to not having permissions when run-as com.myapp.app
new_prefs_path="my_machine.xml"
config="$(cat $new_prefs_path)"
my_app_uri="com.myapp.app"
adb shell "run-as $my_app_uri sh -c 'echo \"$config\" > shared_prefs/on_android.xml'"
This fixes it for me as a bash script. It's made slightly more complicated by needing to be configurable for different apps and complex payloads.
We take a file (could be generated earlier in this script) and read it to a variable.
We then start shell, do run-as my app and run echo expanding the read file to a file in shared_prefs.

Piping output from 1 command to other command in an adb shell command line

I wish to send an adb shell command to an Android device that pipes output from 1 command to other command, but when I try to do that, only the 1st command is executed on the device, and the 2nd one is executed on the host machine. Here's what I tried:
adb shell command1 | command2
command1 is executed on the device, and command2 on the host machine.
How can I get this to work properly?
Thanks!
You could use something like this:
adb shell "command1 | command2"
One way is to create a shell script containing the commands you want and then run the shell script.
Since the Android root filesystem is not writeable at run time (usually, unless you have rooted your device and remount it), you can copy the file to the removable (or emulated) storage, for example /sdcard.
Then run the script using the command adb shell sh /sdcard/your-script-name. Because each script runs in its own subshell, both of your commands will be executed in the same shell on the device (you can confirm it with ps).
adb shell "command1 && command2"
example:
Recursive listing of all files under /system/lib that contain 'foo':
adb shell "cd /system/lib&&ls -lR .|grep -i foo"
The important thing is the double quotes and the double ampersand.
The only thing is that you cannot use it for input as well, meaning that running an executable that requires stdin using one-liner wouldn't work as it requires user intervention.

How to run an adb shell command and remain in the shell?

Does anyone know how to run commands from adb shell and remain in shell session?
What I`m trying to achieve is to set aliases in adb shell.
I have tried the following without success
adb shell <<< "ls"
After executing this command indeed remain in shell, but cannot receive output of any further command.
I have also tried the following:
adb shell <<EOF
ls
EOF
with the same result.
When you run:
adb shell ls
You are running this command currently outside of ADB.
First, you need to enter ADB:
adb shell
Once you enter ADB shell, you can continue to see output and input more commands.
ls
help
To exit ADB, simply type "exit" or hit "Ctrl + C"
expect solution
This will run the command, and leave you in an ADB shell automatically.
adb-cmd
#!/usr/bin/env expect
spawn adb shell
expect "#"
send [ concat [ join $argv " " ] ]
send "\r"
interact
Usage:
adb-cmd 'cd /data/data; ls'
Tested in Ubuntu 16.04 host, Android O guest.
There was a similar question answered in the comments here.
In short, run the following from your terminal:
stty raw -echo ; ( echo "ls" && cat ) | adb shell ; stty sane
Note: without the stty magic, the command is piped to adb and tab complete etc. is not recognized.
//you can use a nodejs script as below:
//let input='shell script here'
let input='cd /data/local/tmp\nchmod +x a.out\n./a.out\n'
p=run("adb.exe",args:'shell',cb:rs=>lg(rs)})
p.stdout.on("data",rs=>{
lg(rs)
setInterval(_=>process.exit(),2000);
})
p.stdin.write(input)
//+++++++++++++++++++++++++
function lg(...args){
console.log(...args);
}
function run(cmd,args,cb){
if(args) cmd= cmd+" "+args;
return require('child_process').exec(cmd,(er,stdout,stderr)=>{
if(er) return cb(stdout||stderr,false);
stdout=stdout==''?true:stdout;
cb(stdout,true)
});
}

Launch a script as root through ADB

I have created a script to mount partitions and do some stuff in my Android system. I saved the script as install.sh in the /bin folder of Android.
I want to call the script from ADB, which is itself called from a batch file on Windows, but it needs to be executed as root.
The first solution I tried was to call the script using
adb shell "su -c sh /bin/script.sh"
but it does not work as it gives me a shell access (with root permissions), but nothing is executed.
I also tried to call
adb root "sh /bin/script.sh"
but I got the following error
adbd cannot run as root in production builds
I then tried to write
su -c "command"
for all the commands which need a root access in my script, but I have the same problem.
When I run the script I only obtain a root shell and nothing is executed.
If I use the first solution by hand (e.g. I call adb shell su, then my script), it works. However the whole point is to automate the process, so that adb shell can be called from another script.
Do you have any idea of how I could achieve this ?
Thanks !
This works for me:
Create myscript.bat and put into it (note the single quotes around the commands to be executed in superuser mode):
adb shell "su -c 'command1; command2; command3'"
then run myscript.bat from a DOS shell.
Note: it doesn't appear that the the DOS line continuation character (^) works in this situation. In other words, the following doesn't work for me:
adb shell "su -c '^
command1; ^
command2; ^
command3'"
This results in "Syntax error: Unterminated quoted string"
This works :
adb shell echo command which needs root privileges \| su
If you need redirection:
adb shell echo 'echo anytext > /data/data/aforbiddenfolder/file' \| su
For "copying" a local file to an android path needing root privileges (but alocalfile must not contain '):
cat alocalfile | adb shell echo "echo '`cat`' > /data/data/aforbiddenfolder/file" \| su
If you have a better way (even for su versions which don't have -c), I am interested.
This works for me:
adb shell "su -c ./data/local/tcpdump-arm -s 0 -v -w /data/local/appxpress_dump.pcap"
I am not sure if I provided a solution or asked for a better one.
I wanted to run some 200 command in batch mode to be sent to adb
I followed this approach
adb shell "su -c command ; "
adb shell "su -c command ; "
adb shell "su -c command ; "
adb shell "su -c command ; "
and I saved them in a batch file
This command
adb shell "su -c 'command1; command2; command3'"
will not work beyond a certain max size . It did not work
error: service name too long
but it does not work as it gives me a shell access (with root permissions), but nothing is executed.
How do you know that you are given root permissions? I assume you are attempting to execute the script on a device? Has your device been rooted?
You may need to give execute permissions via chmod to the file.
chmod ugo=rwx /bin/script.sh
It appears that I was using a very simple version of su which did not accept the -c argument.
I copied another su which did work. AndyD is totally right though, so I am accepting his answer instead of mine :)

Categories

Resources