PSA: Script to copy files to/from Windows/Android adbcopy.bat
From
Maria Sophia@mariasophia@comprehension.com to
alt.comp.os.windows-10,comp.mobile.android,alt.msdos.batch on Mon Jul 27 10:30:45 2026
From Newsgroup: alt.comp.os.windows-10
I wrote this script to solve a problem that USB can't solve.
adbcopy.bat
It accidentally solved a few other common interoperability issues.
Windows <--> Android (over WiFi or USB)
My main problem was that my USB port is broken so I have to use Wi-Fi.
So I originally wrote this script to copy Android:Windows over Wi-Fi.
But then I realized some directories aren't indexed when photos are added.
In that case, WhatsApp / PulseSMS / Signal / Messenger do NOT show it.
a. Because the file is not indexed by MediaStore
b. Or I put it in the wrong folder for those clients to find
c. Or it's owned by the wrong UID
d. Or it's not SAF-visible
e. Or it's not tagged as "user-created
USB drag-n-drop cannot fix this but this script runs a re-indexing.
So the images are immediately available to all the users' media tools.
Also, the script bypasses Android's SAF restrictions, so, for example,
the script can push into folders that USB isn't able to push into, such as
/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images
The script is portable in that it can push files to any phone on the LAN.
The phone can be two floors away yet you can pull & push files to/from it.
Actual output:
adbcopy.bat
Targeting device: 192.168.1.4:5555
----------------
Select Direction:
[1] Android to Windows (Pull Images/Screenshots) [Default]
[2] Windows to Android (Copy/Install APK)
Enter choice [1 or 2]:
1
--- Running Android to Windows Pull ---
What do you want to pull?
[1] Camera photos (SD Card DCIM/Camera)
[2] Screenshots
[3] Camera photos (Internal Storage DCIM/Camera)
[4] Pull directories one-by-one (user selected)
Enter choice [1, 2, 3 or 4]:
... or ...
Enter choice [1 or 2]:
2
--- Windows to Android Push Mode ---
Select file input mode:
[0] Browse for file (no full path needed)
[1] Enter full path manually
Enter choice [0 or 1]: 0
Opening file browser... (you can then pick the file to copy)
Here's the batch file latest version as of today...
:: adbcopy.bat
:: Uses adb on a desktop to copy all image files off my Android phone
:: -------------------------------------------------------------------------
:: v3p4 20260727 Added checks for typos when inputting Windows file paths
:: v3p3 20260727 Added Windows browsing capability if full path is not known
:: v3p2 20260726 Added choice of known Android destinations for convenience
:: v3p1 20260725 Added Windows push to Android using a known full path
:: Added broadcast registration so images can quickly be found
:: v3p0 20260708 added dual-layer deletion (filesystem + mediastore)
:: which prevents FUSE from repeatedly restoring ghost files
:: and which handles private-app sandbox directories
:: v2p9 20260707 fixed quoting and path issues caused by filenames with spaces
:: v2p8 20260707 added skipping of empty directories before prompting user
:: v2p7 20260702 changed to delete only files (not folders)
:: v2p6 20260701 added auto-detect all DCIM subdirectories
:: v2p5 20260701 expanded image file matching
:: v2p4 20260701 added internal DCIM/Camera option
:: v2p3 20260621 open destination folder when done
:: v2p2 20260617 added screenshot directory detection
:: v2p2 20260612 fixed quoting bug in DCIM ls command
:: v2p1 20260607 added Windows-to-Android APK install mode
:: v2p0 20260603 added Screenshot directory support
:: v1p9 20260528 trim trailing spaces from destination path
:: v1p8 20260528 replaced destructive path truncation with CR stripping
:: v1p7 20260528 escaped redirection character in backtick loop
:: v1p6 20260528 updated external SD card path handling
:: v1p5 20260528 fixed null-redirection error and stabilized extraction
:: v1p4 20260528 fixed "unexpected at this time" crash
:: v1p3 20260528 fixed crash on empty variable comparison
:: v1p2 20260528 fixed multi-device detection and nested quoting
:: v1p1 20260528 added debug output for failure cases
:: v1p0 20260528 initial version: copy images from Android by date
:: -------------------------------------------------------------------------
@echo off
setlocal enabledelayedexpansion
cd /d "%~dp0"
set "ADB_EXE=c:\app\editor\android\scrcpy\adb.exe"
:: --- DEVICE DETECTION ---
for /f "skip=1 tokens=1" %%g in ('"%ADB_EXE%" devices') do (
if not "%%g"=="" (
if "!ADB_TARGET!"=="" set "ADB_TARGET=%%g"
)
)
if "!ADB_TARGET!"=="" (
echo [ERROR] No connected ADB devices found.
pause
exit /b
)
echo Targeting device: %ADB_TARGET%
echo ----------------
:: --- MAIN DIRECTION MENU --- default is [1] ---
echo Select Direction:
echo [1] Android to Windows (Pull Images/Screenshots) [Default]
echo [2] Windows to Android (Copy/Install APK)
echo.
set "CHOICE=1"
set /p "CHOICE=Enter choice [1 or 2]: "
if "%CHOICE%"=="2" goto :windows_to_android
if "%CHOICE%"=="1" goto :android_to_windows
goto :android_to_windows
:windows_to_android
echo.
echo --- Windows to Android Push Mode ---
:: begin file selection
echo Select file input mode:
echo [0] Browse for file (no full path needed)
echo [1] Enter full path manually
echo.
set /p "FILE_MODE=Enter choice [0 or 1]: "
if "%FILE_MODE%"=="0" (
echo Opening file browser...
for /f "delims=" %%F in ('powershell -command "Add-Type -AssemblyName System.Windows.Forms; $f=New-Object System.Windows.Forms.OpenFileDialog; $f.Filter='Images|*.jpg;*.jpeg;*.png;*.gif'; if($f.ShowDialog() -eq 'OK'){Write-Output $f.FileName}"') do set "SRC_FILE=%%F"
) else (
:ask_path
set /p "SRC_FILE=Enter full path of file to push: "
if "%SRC_FILE%"=="" (
echo You must enter a full path or choose option 0.
goto ask_path
)
)
if not exist "%SRC_FILE%" (
echo [ERROR] File not found: %SRC_FILE%
pause
exit /b
)
:: end file selection
:: Extract just the filename (e.g., pizzamyheart.jpg)
for %%F in ("%SRC_FILE%") do set "SRC_NAME=%%~nxF"
echo.
echo Select Android destination:
echo [1] /storage/emulated/0/DCIM/Camera
echo [2] /storage/emulated/0/DCIM/Screenshots
echo [3] /storage/emulated/0/Pictures
echo [4] /storage/emulated/0/Pictures/Screenshots
echo [5] /storage/emulated/0/Download
echo [6] /storage/emulated/0/Snapseed
echo [7] /storage/emulated/0/Samsung
echo [8] /storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images
echo.
set /p "DEST_CHOICE=Enter choice [1-8]: "
if "%DEST_CHOICE%"=="1" set "DEST_DIR=/storage/emulated/0/DCIM/Camera"
if "%DEST_CHOICE%"=="2" set "DEST_DIR=/storage/emulated/0/DCIM/Screenshots"
if "%DEST_CHOICE%"=="3" set "DEST_DIR=/storage/emulated/0/Pictures"
if "%DEST_CHOICE%"=="4" set "DEST_DIR=/storage/emulated/0/Pictures/Screenshots"
if "%DEST_CHOICE%"=="5" set "DEST_DIR=/storage/emulated/0/Download"
if "%DEST_CHOICE%"=="6" set "DEST_DIR=/storage/emulated/0/Snapseed"
if "%DEST_CHOICE%"=="7" set "DEST_DIR=/storage/emulated/0/Samsung"
if "%DEST_CHOICE%"=="8" set "DEST_DIR=/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images"
echo.
echo Pushing "%SRC_FILE%" to "%DEST_DIR%"
"%ADB_EXE%" -s %ADB_TARGET% push "%SRC_FILE%" "%DEST_DIR%"
echo Forcing media scan...
"%ADB_EXE%" -s %ADB_TARGET% shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://%DEST_DIR%/%SRC_NAME%
echo Done.
pause
exit /b
:: ROUTINE: ANDROID TO WINDOWS
:android_to_windows
echo.
echo --- Running Android to Windows Pull ---
echo.
echo What do you want to pull?
echo [1] Camera photos (SD Card DCIM/Camera)
echo [2] Screenshots
echo [3] Camera photos (Internal Storage DCIM/Camera)
echo [4] Pull directories one-by-one (user selected)
set "PULL_CHOICE=1"
set /p "PULL_CHOICE=Enter choice [1, 2, 3 or 4]: "
:: Only ask for date if pulling dated files
if "%PULL_CHOICE%"=="4" goto :skip_date
:: --- DATE DETECTION ---
for /f "tokens=2 delims==" %%i in ('wmic os get localdatetime /value') do set "dt=%%i"
set "DETECTED_DATE=%dt:~0,8%"
echo Detected Date: %DETECTED_DATE%
set /p "USER_DATE=Press Enter to confirm, or type a different date (YYYYMMDD): "
if "%USER_DATE%"=="" (
set "TARGET_DATE=%DETECTED_DATE%"
) else (
set "TARGET_DATE=%USER_DATE%"
)
:skip_date
echo.
if "%PULL_CHOICE%"=="4" (
rem Interactive, pulling everything iV no date-based subfolder
set "DEST_DIR=H:\003_camera\20260701"
) else (
rem Date-based pulls still go into YYYYMMDD subfolder
set "DEST_DIR=H:\004_upload\home\pool\assay\%TARGET_DATE%"
)
set /p "USER_DIR=Enter destination directory [Default: %DEST_DIR%]: "
if not "%USER_DIR%"=="" set "DEST_DIR=%USER_DIR%"
:: Remove quotes
set "DEST_DIR=%DEST_DIR:"=%"
:: Trim trailing spaces
:trim
if "!DEST_DIR:~-1!"==" " (
set "DEST_DIR=!DEST_DIR:~0,-1!"
goto trim
)
if not exist "%DEST_DIR%" (
echo Creating directory: %DEST_DIR%
mkdir "%DEST_DIR%"
)
if "%PULL_CHOICE%"=="1" goto :pull_camera
if "%PULL_CHOICE%"=="2" goto :pull_screenshots
if "%PULL_CHOICE%"=="3" goto :pull_camera_internal
if "%PULL_CHOICE%"=="4" goto :pull_interactive_dirs
goto :pull_camera
if "%PULL_CHOICE%"=="1" goto :pull_camera
if "%PULL_CHOICE%"=="2" goto :pull_screenshots
if "%PULL_CHOICE%"=="3" goto :pull_camera_internal
if "%PULL_CHOICE%"=="4" goto :pull_interactive_dirs
goto :pull_camera
:: PULL CAMERA (DCIM)
:pull_camera
echo.
echo Pulling CAMERA images matching *%TARGET_DATE%*.jpg ...
echo Sending files to: "%DEST_DIR%"
echo ----------------
for /f "usebackq delims=" %%i in (`
%ADB_EXE% -s !ADB_TARGET! shell ls /storage/0CA4-352D/DCIM/Camera/*%TARGET_DATE%*.jpg
`) do (
set "FILE_PATH=%%i"
for /f "delims=" %%r in ("!FILE_PATH!") do set "FILE_PATH=%%r"
if not "!FILE_PATH!"=="" (
echo Pulling: !FILE_PATH!
"%ADB_EXE%" -s !ADB_TARGET! pull "!FILE_PATH!" "%DEST_DIR%"
)
)
goto :complete
:: PULL SCREENSHOTS (handle both directories explicitly)
:pull_screenshots
echo.
echo Pulling SCREENSHOTS matching *%TARGET_DATE%*.jpg ...
echo Sending files to: "%DEST_DIR%"
echo ----------------
:: Directory A Android 11 legacy
for /f "usebackq delims=" %%i in (`
%ADB_EXE% -s !ADB_TARGET! shell ls /sdcard/DCIM/Screenshots/*%TARGET_DATE%*.jpg
`) do (
set "FILE_PATH=%%i"
if not "!FILE_PATH!"=="" (
echo Pulling (legacy): !FILE_PATH!
"%ADB_EXE%" -s !ADB_TARGET! pull "!FILE_PATH!" "%DEST_DIR%"
)
)
:: Directory B Android 13 modern
for /f "usebackq delims=" %%i in (`
%ADB_EXE% -s !ADB_TARGET! shell ls /storage/emulated/0/DCIM/Screenshots/*%TARGET_DATE%*.jpg
`) do (
set "FILE_PATH=%%i"
if not "!FILE_PATH!"=="" (
echo Pulling (modern): !FILE_PATH!
"%ADB_EXE%" -s !ADB_TARGET! pull "!FILE_PATH!" "%DEST_DIR%"
)
)
goto :complete
:: PULL CAMERA (INTERNAL STORAGE DCIM)
:pull_camera_internal
echo.
echo Pulling CAMERA images from INTERNAL STORAGE matching *%TARGET_DATE%*.jpg ...
echo Sending files to: "%DEST_DIR%"
echo ----------------
for /f "usebackq delims=" %%i in (`
%ADB_EXE% -s !ADB_TARGET! shell ls /storage/emulated/0/DCIM/Camera/*%TARGET_DATE%*.jpg
`) do (
set "FILE_PATH=%%i"
for /f "delims=" %%r in ("!FILE_PATH!") do set "FILE_PATH=%%r"
if not "!FILE_PATH!"=="" (
echo Pulling: !FILE_PATH!
"%ADB_EXE%" -s !ADB_TARGET! pull "!FILE_PATH!" "%DEST_DIR%"
)
)
goto :complete
:: FINALIZE
:complete
start "" "%DEST_DIR%"
echo Process complete
pause
exit /b
:pull_interactive_dirs
echo.
echo --- Interactive Directory Pull Mode ---
echo Destination: "%DEST_DIR%"
echo.
:: --- Detect ALL folders under /storage/emulated/0/DCIM ---
echo Detecting DCIM folders...
rem Correct quoting: do NOT quote the ls command
"%ADB_EXE%" -s %ADB_TARGET% shell ls -d /storage/emulated/0/DCIM/*/ > "%TEMP%\dcimdirs.txt" 2>&1
echo.
echo --- Interactive Directory Pull Mode (adb uses the FUSE layer) ---
echo Destination: "%DEST_DIR%"
echo.
:: First pull all DCIM subfolders dynamically
for /f "usebackq delims=" %%D in ("%TEMP%\dcimdirs.txt") do (
echo Found DCIM folder: %%D
call :pull_dir "%%D"
)
:: Then pull the other known directories
call :pull_dir "/storage/emulated/0/Pictures"
call :pull_dir "/storage/emulated/0/Download"
call :pull_dir "/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images"
call :pull_dir "/storage/emulated/0/Android/media/net.psyberia.offlinemaps/OfflineMaps Exports"
call :pull_dir "/storage/emulated/0/ReadEra/Covers"
call :pull_dir "/storage/emulated/0/OziExplorer/System Data"
call :pull_dir "/storage/0CA4-352D/DCIM/Camera"
call :pull_dir "/sdcard/DCIM/Screenshots"
goto :eod
:pull_dir
set "DIR=%~1"
echo.
echo Directory: %DIR%
echo Listing files in %DIR% ...
%ADB_EXE% -s %ADB_TARGET% shell ls "%DIR%" > "%TEMP%\filelist_raw.txt"
:: Filter out directories so as to keep only real files
> "%TEMP%\filelist.txt" (
for /f "usebackq delims=" %%Z in ("%TEMP%\filelist_raw.txt") do (
%ADB_EXE% -s %ADB_TARGET% shell "[ -f \"%DIR%/%%Z\" ]" && echo %%Z
)
)
:: Check if any real files exist
set "HASFILES="
for /f "usebackq delims=" %%Z in ("%TEMP%\filelist.txt") do set "HASFILES=1"
if not defined HASFILES (
echo No files found in %DIR%. Skipping.
goto :eod
)
:: Print the actual file list (Option A)
echo Files found:
type "%TEMP%\filelist.txt"
echo.
:: Ask user only if real files exist
set /p "ANS=Pull this directory? [y/N]: "
if /i not "%ANS%"=="y" goto :eod
echo Checking directory: %DIR%
:: Count files
set "COUNT=0"
for /f "usebackq delims=" %%C in ("%TEMP%\filelist.txt") do set /a COUNT+=1
echo Found %COUNT% files in %DIR%.
echo Pulling files...
for /f "usebackq delims=" %%F in ("%TEMP%\filelist.txt") do (
echo Pulling: %DIR%/%%F
%ADB_EXE% -s %ADB_TARGET% pull "%DIR%/%%F" "%DEST_DIR%"
)
:: delete the file and the MediaStore entry and handle space differences
:: Take into account many mediastore naming syntax and access intricacies
set /p "DEL=Delete original files from phone (and MediaStore)? [y/N]: "
if /i "%DEL%"=="y" (
echo Deleting originals and MediaStore entries...
for /f "usebackq delims=" %%F in ("%TEMP%\filelist.txt") do (
rem --- Delete file (handles spaces correctly) ---
echo Deleting file: %DIR%/%%F
"%ADB_EXE%" -s %ADB_TARGET% shell rm "\"%DIR%/%%F\""
rem --- Delete MediaStore entry using full filename (with spaces) ---
echo Deleting MediaStore entry for: %%F
"%ADB_EXE%" -s %ADB_TARGET% shell content delete --uri content://media/external/images/media --where "\"_data LIKE '%/%%F'\""
)
)
:eod
exit /b
REM end of adbcopy.bat
--
On Usenet, some of us share what we have learned so others do not struggle.
--- Synchronet 3.22a-Linux NewsLink 1.2