Debugging what Event is Preventing a Computer from going to Sleep

The saga continues…

On a previous blog post I detailed my attempts to debug Windows 10 and it’s constant issues keeping a computer from being able to sleep peacefully.

Well, I encountered a new issue and I’m about ready to set Microsoft on fire for their lack of QA Testing.

Here are a list of optimal steps I took to find the culprit:

  1. Open a Command Prompt as Administrator
  2. Type powercfg -requests
  3. Look for offending processes and start googling steps to mitigate based off the *.exe name

Well it turned out this time it was a core Windows process that was causing the issue and it seemed that many many many people have reported this since July 2020, see below identifier message.

EXECUTION:
[PROCESS] \Device\HarddiskVolume2\Windows\System32\MoUsoCoreWorker.exe
USO Worker

I didn’t believe in blindly disabling the “Update Orchestrator” as some people suggested – instead I opted to search for what was actually causing the issue and happened upon this solution from /u/MsWolf88 on the below Reddit post.

So here are the steps I took as a result of /u/MsWolf88’s information:

  1. Fresh reboot of Windows 10 computer
  2. In the Start Menu type “Check for Updates” and open it
  3. Check for any outstanding updates and install them – not necessarily the optional updates.
    • If reboots are required as a result of this step do as the prompts say and resume on the next step after all pending updates are satisfied
  4. Close all windows and applications that may be open – specifically the Windows Update window if still open
  5. In the Start Menu type “Services” and open it
  6. Find “Windows Update” in the menu that comes up – right click it and click restart
  7. In the Start Menu type “Check for Updates” and open it
  8. Check for any outstanding updates and install them – not necessarily the optional updates.
    • If reboots are required as a result of this step do as the prompts say and resume on the next step after all pending updates are satisfied

In my case it turned out that their was a hung update that required a restart of the “Windows Update” service but wouldn’t show it was pending despite many reboots (I shut my computer down manually daily) and after triggering the forceful restart of “Windows Update” service it showed the offending update that needed to be applied.

Now, my computer goes to sleep after one minute as God Microsoft Bill Gates intended and no longer has Insomnia until I find the next stupid QA Issue that Microsoft missed.

Simple Log File Handler in Python

For the next time I need to use this…

import logging
import sys

def setup_custom_logger(name):
    formatter = logging.Formatter(fmt='%(asctime)s %(levelname)-8s %(message)s',
                                  datefmt='%Y-%m-%d %H:%M:%S')
    handler = logging.FileHandler('log.txt', mode='w')
    handler.setFormatter(formatter)
    screen_handler = logging.StreamHandler(stream=sys.stdout)
    screen_handler.setFormatter(formatter)
    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)
    logger.addHandler(handler)
    logger.addHandler(screen_handler)
    return logger

>>> logger = setup_custom_logger('myapp')
>>> logger.info('This is a message!')
2015-02-04 15:07:12 INFO     This is a message!
>>> logger.error('Here is another')
2015-02-04 15:07:30 ERROR    Here is another

Credit to paidhima on the below Stack Overflow answer:

Simple Example for Multithreading in Python

Had a need today to create a multithreaded for loop that calls a specific function – trying to speed up a program I wrote as it’s way too slow in a single threaded context but not spin off so many threads that it crashes the computer.

Not enough simple examples for this online – when I did this in C#, I could always find a result within 10 min on Google.

Here’s this for anyone in the future – picture and code below:

from concurrent.futures import ThreadPoolExecutor

#If wanting to not have python take the computer over and slow it down to a crawl
#executor = ThreadPoolExecutor(max_workers=2)
    
#If max_workers is None or not given, it will default to the number of processors on the machine, multiplied by 5, assuming that ThreadPoolExecutor is often used to overlap I/O instead of CPU work and the number of workers should be higher than the number of workers for ProcessPoolExecutor.
executor = ThreadPoolExecutor()

futures = []

path = './test_multi.log'

def testFunction(param1):
    import time
    time.sleep(1)
    with open(path, 'a') as the_file:
        the_file.write("hithere" + param1 + '\n')

items = []

for i in range(1000):
    items.append(str("test " + str(i)))

for item in items:
    a = executor.submit(testFunction, item)
    futures.append(a)

#Wait for all the jobs to finish before moving on
executor.shutdown(wait=True)

#Loop futures to see any failures - only hits exception block if failure detected
for future in futures:
    try:
        future.result()
    except Exception as e:
        print("Detected failed task in threadpool - " + str(e))
        import traceback
        print(traceback.format_exc())

Using Wake on LAN With Google Wifi

Taking the time to write this one up for some other poor soul like me in the future who couldn’t find adequate answers.

Long story short – yes – “Wake on LAN” does work with Google Wifi.

Here are some steps:

  1. Know your IPv4 external address and/or bind it to a Dynamic DNS Service like No-Ip or DuckDNS
    • It is very common for an ISP to just flat give you a new IP address – you have to know this address and understand that if your IPv4 address changes you cannot do a damn thing about it other than mitigate ahead of time
    • We as residential customers have little to no guarantee on our parameters with bandwidth or otherwise – because we’re residential
  2. Have Wake on LAN up and working in a Local IPv4 context first
    • Aka connected to home wifi network – issue the “Magic Packet” – AT HOME
    • If you can’t get it working using your 192.168.* version of your IP while you’re sitting at home – you absolutely have to figure that out first – so stop reading and go figure that out before attempting an external context
  3. Set the following parameters on the Google Wifi Android or iPhone Application:
    • Google Wifi App -> Network & General -> Advanced Networking -> DHCP IP reservations -> Plus Button -> Choose Computer -> Leave Number Default -> Next -> Done
      • Change default if you want but unless you are truly looking for more debug and/or actually require a custom IP number – just leave it alone – no one cares about your fancy 3 digit number other than you – no one will see it – just leave it default
    • Google Wifi App -> Network & General -> Advanced Networking -> Port Management -> Plus Button -> Choose Computer – Use below settings and hit Done
      • Internal Port – 7
      • External Port – 1337
      • Remarks:
        • Internal Port – 7 is default what your computer will listen on locally and/or 9 – but 7 is what my computer responds on
        • External Port – Goal is to choose literally any number other than that is a valid port number – some notes I saw online suggested that our ISP’s have potentially gotten in the habit of straight up blocking these before they even reach the modem – so usage of a nonstandard port fixes this issue
  4. For your test case – I highly suggest starting with the physical numeric IPv4 address before attempting to use any Dynamic DNS solution – just so you know it works bare minumum and can rule out any other variables
  5. Connected to your Home Wifi – go google “what is my current ip address ipv4”
    • Write that down or screenshot it
  6. Disconnected from Home Wifi – Download a Wake on LAN application on your phone and do the below settings based off previous data in this guide
    • I used the app “Wake on LAN” on Android
    • IP/Hostname/Etc – input data from Step 5
    • Port – 1337 (or whatever you customized it to – remember my note around blockage and usage of nonstandard ports)
    • Send WOL packet – should work assuming you followed guidance on Step 2 – if you can’t get Wake On Lan working on your home network first you’re only wasting your own time by adding extra debug steps

Let me know in the comments down below your experience – despite Google Support stating that WOL isn’t supported in a few searches I saw – worked just fine for me.

Debugging what event woke a sleeping Windows Computer

I’m on a mission right now to keep my system OFF as much as possible after the recent failure of my GTX 980.

The Great GPU Shortage of 2020/2021 is no joke – I finally found a GTX 3070 and I intend to keep this card running for years to come. Hindsight though – 7 years from a electrical component is extremely impressive for a computer I used to leave on day in and out.

Shoutout to Henry in Atlanta for being an absolute wonderful human being and selling local AND not scalping – faith in humanity restored!

Right so back to the subject at hand – I’m working on implementing aggressive sleep policies for decreased system up time and overall lower temperatures to kill wear and tear.

I’ve migrated my computer to a cabinet in my living room so it’s absolutely critical that the computer stays off as much as possible in the event someone accidentally closes the cabinet door when it’s not in use.

The below command will show what woke your computer from its slumber last.

powercfg -lastwake

Imagine my surprise when I found out a Hard Drive Imaging/Backup Application that was installed years ago – EaseUs ToDo Backup – was the culprit.

As I continue this journey to peak sleep zen I’ll update this article further with my personal findings.

Recursive pkgdiff for WAR and JAR comparison in Java

This’ll be one of the most useful utilities I’ve written to date

Features:

  • Whitelisting of Desired JAR String Matches
  • Option to enable/disable whitelisting
  • pkgdiff over parent WARs and all children JAR files
  • Option to enable/disable decompiling
    • Decompiles all detected/whitelisted (if enabled) jar files and attaches source back to jar file
    • Used for delta analysis in html report that pkgdiff creates
  • Timestamped Reports – Both Zipped and Regular Folders
    • Each time script is ran clears current working report directory
    • At end of script takes output and copies to timestamped directory
    • At end of script zips the timestamped directory for portability

The idea is to do the following:

  1. Runs pkgdiff on the initial two WAR files
  2. Unzips the WAR files
  3. Searches for any JAR files in each unzipped path and indexes them
  4. Attempts to find a JAR match across both of the unzipped WAR paths
    • Strips version numbers from the jar filenames
    • Checks the whitelist to see if the JAR matches ones you desire to be processed
  5. (Optional) Runs JD Decompiler and attaches the decompiled code back to the JAR
  6. Runs pkgdiff on the matched jar files
  7. Repeat steps 4-6 on all jars
  8. Takes output and copies all to timestamped directory
  9. Zips timestamped directory for portability
    • Does not remove timestamped directory just zips it

Repo URLhttps://github.com/qwertycody/Recursive_PkgDiff_Java

Batch Script to SFTP Download a Log File, Filename Timestamp, and Remotely Tail Log at the end

Another morph of the previous days utility script – does the following:

  1. Download the Remote Log File using SFTP
  2. Copy downloaded file to a timestamped filename
  3. Tail the Downloaded Log file at the end
@echo off
 
REM ######################################################
REM #### BEGIN - Do Not Touch - Misc Setup Variables: ####
REM ######################################################
 
REM SCRIPT_DIRECTORY is defined as the directory that this script currently exists in
SET "SCRIPT_DIRECTORY=%~dp0"
SET "SCRIPT_DIRECTORY=%SCRIPT_DIRECTORY:~0,-1%"
 
for /f "delims=" %%a in ('wmic OS Get localdatetime ^| find "."') do set VARIABLE_DATETIME=%%a
set VARIABLE_YEAR=%VARIABLE_DATETIME:~0,4%
set VARIABLE_MONTH=%VARIABLE_DATETIME:~4,2%
set VARIABLE_DAY=%VARIABLE_DATETIME:~6,2%
set VARIABLE_HOUR=%VARIABLE_DATETIME:~8,2%
set VARIABLE_MINUTE=%VARIABLE_DATETIME:~10,2%
set VARIABLE_SECOND=%VARIABLE_DATETIME:~12,2%
set VARIABLE_TIMESTAMP=%VARIABLE_YEAR%-%VARIABLE_MONTH%-%VARIABLE_DAY%
 
REM ####################################################
REM #### END - Do Not Touch - Misc Setup Variables: ####
REM ####################################################
 
REM #######################################################
REM ### BEGIN - Customized Remote/Local Setup Variables ###
REM #######################################################
 
REM PLINK and PSFTP Application Paths
SET "PLINK=C:/Program Files/Putty/plink.exe"
SET "PSFTP=C:/Program Files/Putty/psftp.exe"
 
REM Setup - Remote Linux Host, Username, and Potential Command to Run
SET "REMOTE_HOSTNAME=hostname.com"
SET "REMOTE_USERNAME=username"
 
REM Setup - Private Key for Authentication
SET "LOCAL_PRIVATE_KEY=%SCRIPT_DIRECTORY%\private_key.ppk"
 
REM Setup - Local File To Transfer - Local Filename Variables including Name, Path, and Timestamp
SET "LOCAL_FILENAME=filename.log"
SET "LOCAL_DIRECTORY=%SCRIPT_DIRECTORY%\"
SET "LOCAL_PATH=%LOCAL_DIRECTORY%\%LOCAL_FILENAME%"
SET "LOCAL_PATH_TIMESTAMPED=%LOCAL_DIRECTORY%\%VARIABLE_TIMESTAMP%_%LOCAL_FILENAME%"
 
REM Setup - Final Transfer Path to Remote Server - Remote Directory and Final Filename to Transmit
SET "REMOTE_FILENAME=filename.log"
SET "REMOTE_DIRECTORY=/remote/log/directory"
SET "REMOTE_PATH=%REMOTE_DIRECTORY%/%REMOTE_FILENAME%"
SET "REMOTE_PATH_TIMESTAMPED=%REMOTE_DIRECTORY%/%VARIABLE_TIMESTAMP%_%REMOTE_FILENAME%"

SET "MISC_COMMAND=tail -f -n+1 %REMOTE_DIRECTORY%/%REMOTE_FILENAME%"

REM #####################################################
REM ### END - Customized Remote/Local Setup Variables ###
REM #####################################################
 
REM #####################################################
REM ### BEGIN - Script Actions Using Setup Variables  ###
REM #####################################################
 
REM File Download and Staging - Remove Current Copies of Local File
del /f "%LOCAL_PATH%"
 
REM SFTP - Stage SFTP Commands in Temp Script
SET "TEMP_BATCH_FILE=%SCRIPT_DIRECTORY%\psftp.batch"   
echo cd %REMOTE_DIRECTORY% > "%TEMP_BATCH_FILE%"
echo get "%REMOTE_FILENAME%" >> "%TEMP_BATCH_FILE%"
 
REM SFTP - Execute Transfer
"%PSFTP%" -i "%LOCAL_PRIVATE_KEY%" ^
         -b "%TEMP_BATCH_FILE%" ^
         "%REMOTE_USERNAME%@%REMOTE_HOSTNAME%"
 
REM SFTP - Remove Temp Script
del /f "%TEMP_BATCH_FILE%"
 
REM Copy File to Timestamp Name 
copy /B /Y "%LOCAL_PATH%" "%LOCAL_PATH_TIMESTAMPED%"
 
REM Issue Command - Tail Live Log
"%PLINK%" -i "%LOCAL_PRIVATE_KEY%" ^
            "%REMOTE_USERNAME%@%REMOTE_HOSTNAME%" ^
            "%MISC_COMMAND%"

REM ###################################################
REM ### END - Script Actions Using Setup Variables  ###
REM ###################################################

pause

Batch Script to SFTP Download an Entire Directory and Timestamp it Locally

This same script I’ve originally written a few days ago is morphing again for another purpose and does the following.

  1. Create a Logs Directory Locally and a Timestamped Sub Directory
  2. SFTP an entire remote directory tree to this Local Directory
@echo off 

REM ###################################################### 
REM #### BEGIN - Do Not Touch - Misc Setup Variables: #### 
REM ###################################################### 

REM SCRIPT_DIRECTORY is defined as the directory that this script currently exists in 
SET "SCRIPT_DIRECTORY=%~dp0" 
SET "SCRIPT_DIRECTORY=%SCRIPT_DIRECTORY:~0,-1%" 

for /f "delims=" %%a in ('wmic OS Get localdatetime ^| find "."') do set VARIABLE_DATETIME=%%a 
set VARIABLE_YEAR=%VARIABLE_DATETIME:~0,4% 
set VARIABLE_MONTH=%VARIABLE_DATETIME:~4,2% 
set VARIABLE_DAY=%VARIABLE_DATETIME:~6,2% 
set VARIABLE_HOUR=%VARIABLE_DATETIME:~8,2% 
set VARIABLE_MINUTE=%VARIABLE_DATETIME:~10,2% 
set VARIABLE_SECOND=%VARIABLE_DATETIME:~12,2% 
set VARIABLE_TIMESTAMP=%VARIABLE_YEAR%-%VARIABLE_MONTH%-%VARIABLE_DAY% 

REM #################################################### 
REM #### END - Do Not Touch - Misc Setup Variables: #### 
REM #################################################### 

REM ####################################################### 
REM ### BEGIN - Customized Remote/Local Setup Variables ### 
REM ####################################################### 

REM PLINK and PSFTP Application Paths 
SET "PLINK=C:/Program Files/Putty/plink.exe" 
SET "PSFTP=C:/Program Files/Putty/psftp.exe" 

REM Setup - Remote Linux Host, Username, and Potential Command to Run 
SET "REMOTE_HOSTNAME=hostname.com" 
SET "REMOTE_USERNAME=username" 

REM Setup - Private Key for Authentication 
SET "LOCAL_PRIVATE_KEY=%SCRIPT_DIRECTORY%\private_key.ppk" 

REM Setup - Local File To Transfer - Local Filename Variables including Name, Path, and Timestamp 
SET "LOCAL_DIRECTORY=%SCRIPT_DIRECTORY%\Logs" 
SET "LOCAL_DIRECTORY_TIMESTAMPED=%LOCAL_DIRECTORY%\%VARIABLE_TIMESTAMP%" 

REM Setup - Final Transfer Path to Remote Server - Remote Directory and Final Filename to Transmit 
SET "REMOTE_DIRECTORY=/remote/path/to/logs" 
 
REM ##################################################### 
REM ### END - Customized Remote/Local Setup Variables ### 
REM ##################################################### 

REM ##################################################### 
REM ### BEGIN - Script Actions Using Setup Variables  ### 
REM ##################################################### 

mkdir "%LOCAL_DIRECTORY%" 
mkdir "%LOCAL_DIRECTORY_TIMESTAMPED%" 

REM Change to Timestamp Directory 
cd /d "%LOCAL_DIRECTORY_TIMESTAMPED%" 

REM SFTP - Stage SFTP Commands in Temp Script 
SET "TEMP_BATCH_FILE=%SCRIPT_DIRECTORY%\psftp.batch"    
echo cd %REMOTE_DIRECTORY% > "%TEMP_BATCH_FILE%" 
echo get -r %REMOTE_DIRECTORY% >> "%TEMP_BATCH_FILE%" 

REM SFTP - Execute Transfer 
"%PSFTP%" -i "%LOCAL_PRIVATE_KEY%" ^ 
         -b "%TEMP_BATCH_FILE%" ^ 
         "%REMOTE_USERNAME%@%REMOTE_HOSTNAME%" 

REM SFTP - Remove Temp Script 
del /f "%TEMP_BATCH_FILE%" 

REM ################################################### 
REM ### END - Script Actions Using Setup Variables  ### 
REM ################################################### 

pause 

Batch Script to Browser Download, Filename Timestamp, SFTP Upload, and Execute Remote SSH Command

If you follow my blog you might have seen a similar blog post the other day where I wrote a script to automatically execute an SSH command and SFTP download a file – using Putty Utilities.

https://garrett.dev/2020/12/09/automatic-script-for-ssh-command-and-sftp-download-in-git-bash/

Now I have retooled that same exact script, except this time it is written in Windows Batch Script format.

The script does the following:

  1. Opens a hard-coded URL in the default web browser and waits for user to press key to continue
    • Practical use case would be downloading a file from OneDrive or any service that doesn’t support direct URL download via curl or wget
  2. Moves the file to the predefined script directory – copies it to a timestamped name of YYYY-MM-DD_filename.extension
  3. Uploads the file using SFTP to the remote server
  4. Issues an SSH Command to copy the remote file to a timestamped name like in previosu step
  5. Issues an SSH Command of your choosing

Overall – this is going to save me loads of time personally with a few tedious tasks and was fun writing it.

@echo off

REM ######################################################
REM #### BEGIN - Do Not Touch - Misc Setup Variables: ####
REM ######################################################

REM SCRIPT_DIRECTORY is defined as the directory that this script currently exists in
SET "SCRIPT_DIRECTORY=%~dp0"
SET "SCRIPT_DIRECTORY=%SCRIPT_DIRECTORY:~0,-1%"

for /f "delims=" %%a in ('wmic OS Get localdatetime ^| find "."') do set VARIABLE_DATETIME=%%a
set VARIABLE_YEAR=%VARIABLE_DATETIME:~0,4%
set VARIABLE_MONTH=%VARIABLE_DATETIME:~4,2%
set VARIABLE_DAY=%VARIABLE_DATETIME:~6,2%
set VARIABLE_HOUR=%VARIABLE_DATETIME:~8,2%
set VARIABLE_MINUTE=%VARIABLE_DATETIME:~10,2%
set VARIABLE_SECOND=%VARIABLE_DATETIME:~12,2%
set VARIABLE_TIMESTAMP=%VARIABLE_YEAR%-%VARIABLE_MONTH%-%VARIABLE_DAY%

REM ####################################################
REM #### END - Do Not Touch - Misc Setup Variables: ####
REM ####################################################

REM #######################################################
REM ### BEGIN - Customized Remote/Local Setup Variables ###
REM #######################################################

REM PLINK and PSFTP Application Paths
SET "PLINK=c:/Program Files/PuTTY/plink.exe"
SET "PSFTP=c:/Program Files/PuTTY/psftp.exe"

REM Setup - Remote Linux Host, Username, and Potential Command to Run
SET "REMOTE_HOSTNAME=hostname.com"
SET "REMOTE_USERNAME=username"
SET "REMOTE_COMMAND=bash -c 'cd /myCoolDirectory/subDirectory && ls -al'"

REM Setup - Private Key for Authentication
SET "LOCAL_PRIVATE_KEY=%SCRIPT_DIRECTORY%\private_key.ppk"

REM Setup - Local File To Transfer - Local Filename Variables including Name, Path, and Timestamp
SET "LOCAL_FILENAME=filename.extension"
SET "LOCAL_DIRECTORY=%SCRIPT_DIRECTORY%\"
SET "LOCAL_PATH=%LOCAL_DIRECTORY%\%LOCAL_FILENAME%"
SET "LOCAL_PATH_TIMESTAMPED=%LOCAL_DIRECTORY%\%VARIABLE_TIMESTAMP%_%LOCAL_FILENAME%"

REM Setup - Final Transfer Path to Remote Server - Remote Directory and Final Filename to Transmit
SET "REMOTE_FILENAME=filename.extension"
SET "REMOTE_DIRECTORY=/myCoolDirectory/subDirectory"
SET "REMOTE_PATH=%REMOTE_DIRECTORY%/%REMOTE_FILENAME%"
SET "REMOTE_PATH_TIMESTAMPED=%REMOTE_DIRECTORY%/%VARIABLE_TIMESTAMP%_%REMOTE_FILENAME%"

REM Setup - Default Download Name from Web Browser - Local Filename That Gets Downloaded From Web Browser
SET "DOWNLOAD_DIRECTORY=%USERPROFILE%\Downloads"
SET "DOWNLOAD_FILENAME=filename.extension"
SET "DOWNLOAD_PATH=%DOWNLOAD_DIRECTORY%\%DOWNLOAD_FILENAME%"

REM #####################################################
REM ### END - Customized Remote/Local Setup Variables ###
REM #####################################################

REM #####################################################
REM ### BEGIN - Script Actions Using Setup Variables  ###
REM #####################################################

REM File Download and Staging - Remove Current Copies of Local File
del /f "%DOWNLOAD_PATH%"
del /f "%LOCAL_PATH%"

REM File Download and Staging - Open URL in Chrome for Download
REM
REM If URL contains % paste in Notepad++ - find replace % with %% to properly escape
REM
SET "URL_TO_OPEN=https://sharepoint.com/filename.extension%%tokenThing%%ect"
start "" "%URL_TO_OPEN%"
echo "Press any key when file is finished downloading..."
pause

REM File Download and Staging - Move Downloaded File to Script Directory
move /y "%DOWNLOAD_PATH%" "%LOCAL_PATH%"
copy /B /Y "%LOCAL_PATH%" "%LOCAL_PATH_TIMESTAMPED%"

REM SFTP - Stage SFTP Commands in Temp Script
SET "TEMP_BATCH_FILE=%SCRIPT_DIRECTORY%\psftp.batch" 	
echo cd %REMOTE_DIRECTORY% > "%TEMP_BATCH_FILE%"
echo put "%LOCAL_FILENAME%" >> "%TEMP_BATCH_FILE%"

REM SFTP - Execute Transfer
"%PSFTP%" -i "%LOCAL_PRIVATE_KEY%" ^
		 -b "%TEMP_BATCH_FILE%" ^
		 "%REMOTE_USERNAME%@%REMOTE_HOSTNAME%" 

REM SFTP - Remove Temp Script
del /f "%TEMP_BATCH_FILE%"

REM Issue Command - Remote Copy of Transferred File to Timestamp File
SET "REMOTE_TIMESTAMP_COMMAND=cp '%REMOTE_PATH%' '%REMOTE_PATH_TIMESTAMPED%'"
"%PLINK%" -i "%LOCAL_PRIVATE_KEY%" ^
			"%REMOTE_USERNAME%@%REMOTE_HOSTNAME%" ^
			"%REMOTE_TIMESTAMP_COMMAND%"

REM Issue Command - Execute Misc Command
"%PLINK%" -i "%LOCAL_PRIVATE_KEY%" ^
			"%REMOTE_USERNAME%@%REMOTE_HOSTNAME%" ^
			"%REMOTE_COMMAND%"

REM ###################################################
REM ### END - Script Actions Using Setup Variables  ###
REM ###################################################

Automatic Script for SSH Command and SFTP Download In Git Bash

Here’s a script I wrote today to automatically execute a command over SSH using Putty’s PLINK and subsequently download a file using Putty’s PSFTP from a remote Linux host.

My exact use case is using this to execute a remote command that pulls a file from a Docker container to a temp directory. Then I download that file to my local Windows computer.

This pairs well with another blog post I wrote that allows you to automatically set up a public/private key pair and simply copy paste to a notepad located at the below link.

https://garrett.dev/2019/06/13/bash-script-for-automatic-creation-and-generation-of-public-private-key-for-linux-server-login/

Automate everything – that’s my philosophy.

#Ensure nothing happens outside the directory this script is ran from
cd "$(dirname "$0")"
SCRIPT_DIRECTORY=$(pwd)

REMOTE_HOSTNAME="hostname.com"
REMOTE_USERNAME="username"

REMOTE_COMMAND="bash -c 'cd /remoteDirectory/subDirectory/ && sh getFromDockerContainer.sh'"

#Double slash is for git bash path conversion compatibility
REMOTE_FILENAME="//remoteDirectory/subDirectory/file.extension"
LOCAL_FILENAME="$SCRIPT_DIRECTORY/file.extension"

LOCAL_PRIVATE_KEY="$SCRIPT_DIRECTORY/private_key.ppk"

PLINK="/c/Program Files/PuTTY/plink.exe"
PSFTP="/c/Program Files/PuTTY/psftp.exe"

rm -f "$LOCAL_FILENAME"

"$PLINK" -i "$LOCAL_PRIVATE_KEY" "$REMOTE_USERNAME@$REMOTE_HOSTNAME" "$REMOTE_COMMAND"

TEMP_BATCH_FILE="$SCRIPT_DIRECTORY/psftp.batch"

echo "get $REMOTE_FILENAME" > "$TEMP_BATCH_FILE"

"$PSFTP" -i "$LOCAL_PRIVATE_KEY" -b "$TEMP_BATCH_FILE" "$REMOTE_USERNAME@$REMOTE_HOSTNAME"

rm -f "$TEMP_BATCH_FILE"