I need to calculate md5 hash of files in a directory hierarchy. I am using the following case as a test. The Android device I have has a md5 binary, but needs absolute path of file (md5 <filename>).
I have the following directory hierarchy on a Android device:
/data/local/tmp/test1
/data/local/tmp/test1/test2
/data/local/tmp/test1/test2/test3
To get list of absolute paths, I followed the answer mentioned here.
$ adb shell 'function rcrls() { ls -d $1/* | while read f; do echo "$f"; if [ -d "$f" ]; then rcrls "$f"; fi; done } ; rcrls /data/local/tmp/' > filelist.txt
Now I have list of absolute paths of each file.
Next I want to read this file in a script line by line, and call md5 for each line. md5 will print a message if the input is a directory. I followed the example here.
#! /bin/bash
filename='filelist.txt'
cat $filename | while read LINE; do
adb shell 'md5 $LINE'
done
I get the following output:
/data/local/tmp/test1/test2
/data/local/tmp/test1/test2/test3
could not read /data/local/tmp/test1, Is a directory
I expected it to print a directory warning for test1 and test2, and then md5 for test3, as test3 is a file. Can someone suggest how to fix this code ? I was expecting something like:
could not read /data/local/tmp/test1, Is a directory
could not read /data/local/tmp/test1/test2, Is a directory
<hash_value> /data/local/tmp/test1/test2/test3
You should use find:
adb shell find /data/local/tmp -type f -exec md5sum {} '\;'
Related
myapps.txt - contains the list of all packages found through adb shell pm list packages > myapps.txt
package:com.flipkart.android
package:com.android.certinstaller
package:com.android.carrierconfig
package:com.reddit.frontpage
package:com.wapi.wapicertmanage
package:com.brave.browser
Following is the code I wrote in the batch script to copy all the apps in one go from android to PC using ADB.
Secondly, I'm splitting my string by colon(:) such that for example -
string1 contains package and
string2 contains com.google.android.youtube
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1* delims=:" %%i in (myapps.txt) do (
echo j: %%j
set string2=%%j
adb shell pm path !string2! > tmp.txt
set /p new=< tmp.txt
#echo on
#echo new: !new!
#echo off
set "str=%new%"
set "string1=%str::=" & set "string3=%"
del tmp.txt REM delete file after reading from it.
REM creating new folder for each app
mkdir apps_%input%\%string2%
REM pulling app from Android to PC
adb pull %string3% apps_%input%\%string2%
set /a count+=1
echo Done !count!
)
Here's the following output after 2 executions of for loop I'm getting
j: com.flipkart.android
new: package:/data/app/com.flipkart.android-XOmoiAws7zOd07eM1nZIlg==/base.apk
A subdirectory or file apps_2\ already exists.
adb: error: failed to stat remote object 'apps_2\': No such file or directory
Done 1
j: com.android.certinstaller
new: package:/system/app/CertInstaller/CertInstaller.apk
A subdirectory or file apps_2\ already exists.
adb: error: failed to stat remote object 'apps_2\': No such file or directory
Done 2
Please help me why I'm getting this output. Also apps_2 don't exist before execution how it's prompting that it already exists.
But,the same thing is working perfectly in cmd prompt:
mkdir apps_2\com.flipkart.android
adb pull /data/app/com.flipkart.android-XOmoiAws7zOd07eM1nZIlg==/base.apk apps_2\com.flipkart.android
here's the output I received after execution
/data/app/com.flipkart.android-XOmoiAws7zOd07eM1nZIlg==/base.apk: 1 file pulled. 33.8 MB/s (12691794 bytes in 0.358s)
The batch file is not working as expected because of delayed expansion is used only for some, but not all environment variable references inside the FOR command block. Only the environment variable input can be referenced with %input% inside the command block as it is the only environment variable defined outside the command block and not modified inside the command block. All other environment variables are defined/modified inside the command block and referenced inside the command block. For that reason all environment variables except input must be referenced with using ! instead of %.
However, the usage of environment variables is not needed at all for this task.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
if not defined input set "input=1"
set "count=0"
if exist myapps.txt for /F "tokens=2 delims=:" %%I in (myapps.txt) do (
echo Getting path of app "%%I" ...
for /F "tokens=2 delims=:" %%J in ('adb.exe shell pm path "%%I" 2^>nul') do (
echo Path of app %%I is: "%%J"
rem Creating new folder for each app.
mkdir "apps_%input%\%%I" 2>nul
if exist "apps_%input%\%%I\" (
rem Pulling app from Android to PC.
echo Pulling app "%%I" to "apps_%input%\%%I" ...
adb.exe pull "%%J" "apps_%input%\%%I"
set /A count+=1
) else echo ERROR: Failed to create directory: "apps_%input%\%%I\"
)
)
if %count% == 1 (set "PluralS=") else set "PluralS=s"
echo Pulled %count% app%PluralS% from Android to PC.
endlocal
The outer FOR reads one line after the other from myapps.txt. Each line is split up into substrings using the colon as delimiter because of option delims=:. The first colon delimited substring is always package which is of no interest for this task. Therefore the option tokens=2 is used to assign the second substring like com.flipkart.android to the specified and case-sensitive loop variable I.
The inner FOR loop starts in background one more command process with %ComSpec% /c and the command line in the parentheses appended as additional argument. The output of adb to handle STDOUT of the background command process is captured by FOR and processed line by line after started cmd.exe closed itself after adb terminated itself.
The single line output by adb is again split up into substrings using colon as delimiter with assigning again just the second substring to specified loop variable J.
Next a subdirectory is created with application name as directory name with redirecting the error message output on directory already existing or failed to create from handle STDERR to device NUL to suppress it.
Then an existence check for the just created directory is made to verify if it really exists and if this is the case the application is pulled from Android device to PC.
The help output on running cmd /? in a command prompt window explains on last page that a file name (or any other argument string) containing a space or of these characters &()[]{}^=;!'+,`~ must be enclosed in double quotes. For that reason all argument strings referencing the value assigned currently to the loop variables I (app name) and J (app path) are enclosed in ".
All echo command lines and the last if condition can be removed as they are just for getting some progress information during execution of the batch file.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
for /?
if /?
mkdir /?
rem /?
set /?
setlocal /?
Read the Microsoft article about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded adb command line with using a separate command process started in background.
Story
I take photos and record videos with my phone camera and keep all of them on my internal storage/sdcard. I periodically back them up on my PC, so I keep these camera photos on PC storage in sync with phone storage.
For years, I've been backing up my phone camera photos to my PC in the following way:
Plug in phone into PC and allow access to phone data
Browse phone storage → DCIM → Camera
Wait several minutes for the system to load a list of ALL photos
Copy only several latest photos which haven't been backed up yet
I figured that waiting several minutes for all photos to load is an unnecessary drag so I downloaded adb platform tools. I've added the folder bin to my Path environment variable (i.e. %USERPROFILE%\Tools\adb-platform-tools_r28.0.3) so that I can seamlessly use adb and not write its full path each time.
The script
I wrote the following script for Git Bash for Windows. It is also compatible with Unix if you change the $userprofile variable. Essentially, the script pulls camera photos between two dates from phone storage to PC.
# Attach device and start deamon process
adb devices
# Initialize needed variables
userprofile=$(echo "$USERPROFILE" | tr "\\" "/") # Windows adjustments
srcFolder="//storage/06CB-C9CE/DCIM/Camera" # Remote folder
dstFolder="$userprofile/Desktop/CameraPhotos" # Local folder
lsFile="$dstFolder/camera-ls.txt"
filenameRegex="2019061[5-9]_.*" # Date from 20190615 to 20190619
# Create dst folder if it doesn't exist
mkdir -p "$dstFolder"
# 1. List contents from src folder
# 2. Filter out file names matching regex
# 3. Write these file names line by line into a ls file
adb shell ls "$srcFolder" | grep -E "$filenameRegex" > "$lsFile"
# Pull files listed in ls file from src to dst folder
while read filename; do
if [ -z "$filename" ]; then continue; fi
adb pull "$srcFolder/$filename" "$dstFolder" # adb: error: ...
done < "$lsFile"
# Clean up
rm "$lsFile"
# Inform the user
echo "Done pulling files to $dstFolder"
The problem
When I run the script (bash adb-pull-camera-photos.sh), everything runs smoothly except for the adb pull command in the while-loop. It gives the following error:
': No such file or directoryemote object '//storage/06CB-C9CE/DCIM/Camera/20190618_124656.jpg
': No such file or directoryemote object '//storage/06CB-C9CE/DCIM/Camera/20190618_204522.jpg
': No such file or directoryemote object '//storage/06CB-C9CE/DCIM/Camera/20190619_225739.jpg
I am not sure why the output is broken. Sometimes when I resize the Git Bash window some of the text goes haywire. This is the actual error text:
adb: error: failed to stat remote object '//storage/06CB-C9CE/DCIM/Camera/20190618_124656.jpg': No such file or directory
adb: error: failed to stat remote object '//storage/06CB-C9CE/DCIM/Camera/20190618_204522.jpg': No such file or directory
adb: error: failed to stat remote object '//storage/06CB-C9CE/DCIM/Camera/20190619_225739.jpg': No such file or directory
I am sure that these files exist in the specified directory on the phone. When I manually execute the failing command in bash, it succeeds with the following output:
$ adb pull "//storage/06CB-C9CE/DCIM/Camera/20190618_124656.jpg" "C:/Users/User/Desktop/CameraPhotos/"
//storage/06CB-C9CE/DCIM/Camera/20190618_124656.jpg: 1 file pulled. 15.4 MB/s (1854453 bytes in 0.115s)
The question
I can't figure out what's wrong with the script. I thought the Windows system might be causing a commotion, because I don't see the reason why the same code works when entered manually, but doesn't work when run in a script. How do I fix this error?
Additional info
Note that I had to use // in the beginning of an absolute path on Windows because Git Bash would interpret / as its own root directory (C:\Program Files\Git).
I've echoed all variables inside the script and got all the correct paths that otherwise work via manual method.
camera-ls.txt file contents
20190618_124656.jpg
20190618_204522.jpg
20190619_225739.jpg
Additional questions
Is it possible to navigate to external sdcard without using its name? I had to use /storage/06CB-C9CE/ because /sdcard/ navigates to internal storage.
Why does tr "\\" "/" give me this error: tr: warning: an unescaped backslash at end of string is not portable?
Windows batch script
Here's a .bat script that can be run by Windows Command Prompt or Windows PowerShell. No Git Bash required.
:: Start deamon of the device attached
adb devices
:: Pull camera files starting from date
set srcFolder=/storage/06CB-C9CE/DCIM/Camera
set dstFolder=%USERPROFILE%\Desktop\CameraPhotos
set lsFile=%USERPROFILE%\Desktop\CameraPhotos\camera-ls.txt
set dateRegex=2019061[5-9]_.*
mkdir %dstFolder%
adb shell ls %srcFolder% | adb shell grep %dateRegex% > %lsFile%
for /F "tokens=*" %%A in (%lsFile%) do adb pull %srcFolder%/%%A %dstFolder%
del %lsFile%
echo Done pulling files to %dstFolder%
Just edit the srcFolder to point to your phone camera folder,
plug a pattern into the dateRegex for matching the date interval and
save it as a file with .bat extension, i.e: adb-pull-camera-photos.bat.
Double-click the file and it will pull filtered photos into CameraPhotos folder on Desktop.
Keep in mind that you still need have adb for Windows on your PC.
The problem was with Windows line delimiters.
Easy fix
Just add the IFS=$'\r\n' above the loop so that the read command knows the actual line delimiter.
IFS=$'\r\n'
while read filename; do
if [ -z "$filename" ]; then continue; fi
adb pull "$srcFolder/$filename" "$dstFolder"
done < "$lsFile"
Explanation
I tried plugging the whole while-loop into the console and it failed with the same error:
$ bash adb-pull-camera-photos.sh
List of devices attached
9889db343047534336 device
tr: warning: an unescaped backslash at end of string is not portable
': No such file or directoryemote object '//storage/06CB-C9CE/DCIM/Camera/20190618_124656.jpg
': No such file or directoryemote object '//storage/06CB-C9CE/DCIM/Camera/20190618_204522.jpg
': No such file or directoryemote object '//storage/06CB-C9CE/DCIM/Camera/20190619_225739.jpg
Done pulling files to C:/Users/User/Desktop/CameraPhotos
This time I started investigating why the output was broken. I remembered that windows uses \r\n as newline, which means Carriage Return + Line Feed, (CR+LF), so some text must have been overwritten.
It was because of broken values stored inside the $filename variable.
This is the loop from the script:
while read filename; do
if [ -z "$filename" ]; then continue; fi
adb pull "$srcFolder/$filename" "$dstFolder"
done < "$lsFile"
Since each iteration of the while-loop reads a line from $lsFile in the following form:
exampleFilename.jpg\r\n
It misinterprets the newline symbols as part of the file name, so adb pull tries to read files with these whitespaces in their names, but fails and it additionally writes a broken output.
Adb Photo Sync
This might not be the answer but might be useful for others looking for android photo/files backup solution.
I use this script on my Windows with git bash. This can be easily used for Linux. A common issue with a long backup process is that it might get interrupted and you might have to restart the entire copy process from start.
This script saves you from this trouble. You can restart the script or interrupt in between but it will resume copy operation from the point it left.
Just change the rfolder => android folder, lfolder => local folder
#!/bin/sh
rfolder=sdcard/DCIM/Camera
lfolder=/f/mylocal/s8-backup/Camera
adb shell ls "$rfolder" > android.files
ls -1 "$lfolder" > local.files
rm -f update.files
touch update.files
while IFS= read -r q; do
# Remove non-printable characters (are not visible on console)
l=$(echo ${q} | sed 's/[^[:print:]]//')
# Populate files to update
if ! grep -q "$l" local.files; then
echo "$l" >> update.files
fi
done < android.files
script_dir=$(pwd)
cd $lfolder
while IFS= read -r q; do
# Remove non-printable characters (are not visible on console)
l=$(echo ${q} | sed 's/[^[:print:]]//')
echo "Get file: $l"
adb pull "$rfolder/$l"
done < "${script_dir}"/update.files
I am new to Shell Scripting. Could you please help me to write code for below requirement? I tried using for loops but it did not work.
I have Folder "ApkFiles" and It has 100 .apk files
I need to run the below commands on all those .apk files in the folder
echo nameOfEachApk >> minTarget.xls
aapt.exe d badging eachAPKName.apk | grep -E 'sdkVersion|targetSdkVersion' >> minTarget.xls
I appreciate all the help.
This will iterate over the directory the python folder is located in. Simply put this in a .py file in your apkfiles directory and run it. I can't test it since I don't have your exact setup but it should do what you are asking.
from subprocess import call
import os
# Get current working directory
cwd = os.getcwd()
# Get a list of files in the current working directory
f = open("minTarget.xls")
for apkFile in os.listdir(cwd):
# These call commands will run the shell commands you need
# The [:4] strips off the .apk from the file string
call(["echo", apkFile[:-4], ">>", "minTarget.xls"], stdout=f)
call(["aapt.exe", "d", "badging", apkFile, "|", "grep", "-E", "'sdkVersion|targetSdkVersion'", ">>", "minTarget.xls"], stdout=f)
f.close()
To see how an explanation of how I am calling the shell commands look at the subprocess module
I was wondering about how to create / extract / verify .tar.md5 files. These files are used when flashing images to android devices, see here for example.
As far as I can tell the checksum is appended to the file like this:
cp file.tar file.tar.md5
md5sum file.tar >> file.tar.md5
Firstly I would like to know how to extract the file. Can I simply use tar -xf on the file.tar.md5?
How can I verify the integrity of the file? I would like to remove the last bytes (containing the checksum) from the file to obtain the original file back. I guess you would have to use a regexp to match the checksum file.tar? Is something like this implemented somewhere already?
First of all tar -xf should work since tar continues while it matches its' packing algorithm. If the file stops matching so would tar.
Also most archive managers such as 7-zip or winrar will open it if you remove the ".md5".
They might print error regarding mismatch with the end of the file, ignore it.
As for verifying the file:
print out the stored md5sum: tail -z -n 1 [File name here].tar.md5
calculate the md5sum of the tar part of the file: head -z -n -1 [File name here].tar.md5 | md5sum
What works for me with Ubuntu 19.10 is:
download single-file 4 GiB zip from sammobile com
unzip to several *.tar.md5
run the below command-line
.
for F in *.tar.md5; do echo -n "$F " &&
EXP=($(tail --lines=1 "$F")) &&
ACT=($(head --lines=-1 "$F" | md5sum)) &&
if [ ${EXP[0]} = ${ACT[0]} ]; then echo -n "md5ok " &&
tar --extract --file "$F" && echo "done"
else echo "FAIL"; fi; done &&
unlz4 --multiple --verbose *.lz4
AP_G965U1UEU3ARL1_CL14745140_QB21029084_REV01_user_low_ship_MULTI_CERT_meta.tar.md5 md5ok done
BL_G965U1UEU3ARL1_CL14745140_QB21029084_REV01_user_low_ship_MULTI_CERT.tar.md5 md5ok done
CP_G965U1UEU3ARL1_CP11407818_CL14745140_QB21029084_REV01_user_low_ship_MULTI_CERT.tar.md5 md5ok done
CSC_OMC_OYM_G965U1OYM3ARL1_CL14745140_QB21029084_REV01_user_low_ship_MULTI_CERT.tar.md5 md5ok done
HOME_CSC_OMC_OYM_G965U1OYM3ARL1_CL14745140_QB21029084_REV01_user_low_ship_MULTI_CERT.tar.md5 md5ok done
…
But we should all try to get away from bash
I am trying to write a bash script that decompiles several .apk files using apktool. Each apk file is located in a subdirectory of the sample folder.
#!bin/bash
for item in $(ls samples);
do
for apk in $(ls "samples/$item");
do
echo ./apktool/apktool d "./samples/$item$apk"
$(./apktool/apktool d "./samples/$item$apk")
done
done
When I run the script I get the following output:
./apktool/apktool d ./samples/ADRD/53dc.apk*
Input file (./samples/ADRD/53dc.apk*) was not found or was not readable.
The input file error message is the standard for when apktool cannot find a file. However, if I run the following command in the terminal the apktool will work correctly.
./apktool/apktool d ./samples/ADRD/53dc.apk*
I have changed the permissions of all the files located in the samples folder to rw for all users. I also have tried using sudo with the shell script, but this causes the script to hang. However, when I use sudo with the apktool in the command line it also hangs. Therefore, I am not sure if using sudo with apktool is doable.
Any help is appreciated, thanks.
So it looks like this ls gives you an output with an asterisk * appended at the end of the apk filename, because the file is executable.
for apk in $(ls "samples/$item");
This is not the default behaviour of ls, you are getting this probably because you have aliased ls to ls -F or similar. To bypass the alias, rewrite the line this way:
for apk in $(\ls "samples/$item");
Notice the \ I added there.
BTW, is it normal that an apk file is executable? Perhaps you can remove the executable bit:
find samples -name '*.apk' -exec chmod -x {} \;
Also, possibly your script can be replaced with this one liner:
find samples -name '*.apk' -exec ./apktool/apktool d {} \;
Mind you, this is not exactly the same thing, because it may go deeper than two directories. If you need to limit the depth, that's possible too, see man find