Making my programming experience in Windows as nice as programming in Linux.
Windows 11 is my daily driver. I have a Dell Latitude E6440 that runs
Fedora wonderfully, but I just haven't been using it very much recently. So I
spent some time minimising the friction of working in Windows. I ended up with a
.bashrc file that works in both Windows and Linux.
One day, I'll get around to trying out WSL [1], but for now, I'm working with
Git Bash and ConEmu [2].
I found using a Makefile convenient for storing Pelican invocations (build for
dev, host locally and rebuild for dev on save, build for production). Lucky
for me, Windows 10 and Windows 11 come with the Windows Package Manager
winget [3], so installing make is as simple as:
I found that ConEmu was hanging after I exited the last shell. It turned out
that invoking ssh-agent from .bashrc caused ConEmu to hang around—I
guess it was waiting for ssh-agent to exit.
The trick is to get ConEmu to start ssh-agent as a task alongside
Git Bash [4]. ssh-add looks for the variables SSH_AUTH_SOCK and
SSH_AGENT_PID, but I couldn't get ConEmuC to pass them on to Git Bash,
so I just modified the batch script to generate a bash script that exports the
variables...
@echo off
rem Delayed expansion causes !variable! to be re-interpreted each time it is encounteredsetlocal EnableDelayedExpansion
rem Is ssh-agent.exe currently running?
tasklist /fi "IMAGENAME eq ssh-agent.exe"| find /i /n "ssh-agent.exe">NUL
iferrorlevel1(rem No - need to run itrem Set the location of ssh-agent.exe hererem Run ssh-agent.exe and pipe the output to the file .ssh_agent_info"%USERPROFILE%\AppData\Local\Programs\Git\usr\bin\ssh-agent.exe" -c >".ssh_agent_info"iferrorlevel1(echo"Couldn't start ssh-agent"exit /b 1
)rem Set environment variables so that SSH applications such as ssh-add.exerem know how to to connect to the SSH agentfor/f"eol=; tokens=2,3*"%%i in(.ssh_agent_info)do(if"%%i"=="SSH_AUTH_SOCK"(set"SSH_AUTH_SOCK=%%j")if"%%i"=="SSH_AGENT_PID"(set"SSH_AGENT_PID=%%j"))rem Remove semicolon at the endset"SSH_AUTH_SOCK=!SSH_AUTH_SOCK:~0,-1!"set"SSH_AGENT_PID=!SSH_AGENT_PID:~0,-1!"echo SSH_AUTH_SOCK=!SSH_AUTH_SOCK!>.ssh_agent_info
echo SSH_AGENT_PID=!SSH_AGENT_PID!>>.ssh_agent_info
echo export SSH_AUTH_SOCK >>.ssh_agent_info
echo export SSH_AGENT_PID >>.ssh_agent_info
)endlocal
...and then .bashrc looks for the script and runs it. Now I can call ssh-add
and any keys I add will be available from all sessions within ConEmu.
1 2 3 4 5 6 7 8 91011121314
# Start ssh-agentifcommand -v "ssh-agent"&>/dev/null;then# Is ssh-agent running? Count ps aux lines that contain ssh-agent# (This code should work on both Windows and *nix)if[`ps aux | grep ssh-agent | grep -v grep | wc -l`="0"];then# ssh-agent is not running; run it
ssh-agent >.ssh_agent_info 2>/dev/null
fiif["$SSH_AGENT_PID"==""]&&[ -f .ssh_agent_info ];then# Set $SSH_AGENT_PID and $SSH_AUTH_SOCKsource .ssh_agent_info
fifi
At the time of writing, 1Password has an SSH agent [5], but it requires
Windows Hello, which I don't want to use. The 1Password team is working on
a solution that doesn't require Windows Hello, though, so I might have to
tweak my setup again in the future...
When attempting to transfer files via scp, I received the error message below:
scp: Received message too long 1097295214
scp: Ensure the remote shell produces no output for non-interactive sessions.
It turns out that the .bashrc installed on the remote host was causing scp
to fail because the script generated by ssh-agent includes a line to print the
process ID of ssh-agent[6].
10111213
if["$SSH_AGENT_PID"==""]&&[ -f .ssh_agent_info ];then# Set $SSH_AGENT_PID and $SSH_AUTH_SOCKsource .ssh_agent_info
fi
My project files are on the D: drive. I got tired of typing cd /d/projects,
so I created a symlink using mklink in an Command Prompt as Administrator. I
didn't know that you could do that!
# Bash optionsHISTCONTROL=ignoredups # Ignore duplicate entriesHISTSIZE=1000# 1000 linesshopt -s histappend # Append to history fileshopt -s checkwinsize # Tell Bash to check the window size after each command# Aliasesaliasls='ls -hF --color=auto'if uname -a | grep MINGW >/dev/null 2>&1;then# Allow Python to run in Git Bashaliaspython='winpty python'aliasvenv_activate='source .venv/Scripts/activate'elsealiasvenv_activate='source .venv/bin/activate'fiifcommand -v bc &>/dev/null;thenaliasbc='bc -l'fiifcommand -v xclip &>/dev/null;thenaliasxclip='xclip -selection clipboard'fi# Start ssh-agentifcommand -v "ssh-agent"&>/dev/null;then# Is ssh-agent running? Count ps aux lines that contain ssh-agent# (This code should work on both Windows and *nix)if[`ps aux | grep ssh-agent | grep -v grep | wc -l`="0"];then# ssh-agent is not running; run it
ssh-agent >.ssh_agent_info 2>/dev/null
fiif["$SSH_AGENT_PID"==""]&&[ -f .ssh_agent_info ];then# Set $SSH_AGENT_PID and $SSH_AUTH_SOCKsource .ssh_agent_info >/dev/null 2>&1fifi# https://www.ditig.com/256-colors-cheat-sheet
_set_ps1(){if[["$TERM"=~ 256color ]];thenlocalc1='\[\033[38;5;044m\]'# DarkTurquoiselocalc2='\[\033[38;5;160m\]'# Red3localc3='\[\033[38;5;034m\]'# Green3localc4='\[\033[38;5;247m\]'# Grey62elselocalc1='\[\033[36m\]'# Cyanlocalc2='\[\033[31m\]'# Redlocalc3='\[\033[32m\]'# Greenlocalc4='\[\033[37m\]'# Greyfilocalc0='\[\033[0m\]'# Default colourexportPS1="\$c1\u@\h \$c0\w \$c1\$(_get_git_branch)\$c4\$(_get_git_no_untracked)\$c2\$(_get_git_untracked)\$c4\$(_get_git_no_uncommitted)\$c2\$(_get_git_uncommitted)\$c4\$(_get_git_no_committed)\$c3\$(_get_git_committed)\$c0\\$ "}
_get_git_branch(){# Print branch name, if anylocalbranch=`git branch 2>/dev/null | grep '^*'| cut -d' ' -f2-`[[ -n "$branch"]]&&echo -n "$branch"}
_get_git_no_untracked(){# Empty circlelocalcharacter="\xE2\x97\x8B"# Ensure we are in a git repoif git b &>/dev/null;then# Print the character if there AREN'T any untracked filesif ! git status 2>/dev/null | grep -q 'Untracked files'2>/dev/null;then# Space in front to separate it from the branch nameecho -e " $character"fifi}
_get_git_untracked(){# Empty circlelocalcharacter="\xE2\x97\x8B"# Print the character if there ARE untracked filesif git status 2>/dev/null | grep -q 'Untracked files'2>/dev/null;then# Space in front to separate it from the branch nameecho -e " $character"fi}
_get_git_no_uncommitted(){# Filled circlelocalcharacter="\xE2\x97\x8F"# Ensure we are in a git repoif git b &>/dev/null;then# Print the character if there AREN'T any unstaged changesif ! git status 2>/dev/null | grep -q 'Changes not staged for commit'&>/dev/null;thenecho -e "$character"fifi}
_get_git_uncommitted(){# Filled circlelocalcharacter="\xE2\x97\x8F"# Print the character if there ARE unstaged changesif git status 2>/dev/null | grep -q 'Changes not staged for commit'2>/dev/null;thenecho -e "$character"fi}
_get_git_no_committed(){# Filled circlelocalcharacter="\xE2\x97\x8F"# Ensure we are in a git repoif git b &>/dev/null;then# Print the character if there AREN'T any changes that have been added for commitif ! git status 2>/dev/null | grep -q 'Changes to be committed'&>/dev/null;thenecho -e "$character"fifi}
_get_git_committed(){# Filled circlelocalcharacter="\xE2\x97\x8F"# Print the character if there ARE changes that have been added for commitif git status 2>/dev/null | grep -q 'Changes to be committed'2>/dev/null;thenecho -e "$character"fi}
_set_ps1
[6] "Put simply, .bashrc, .bash_profile, .cshrc, .profile, etc., have to be silent for non-interactive sessions or they interfere with the sftp / scp connection protocol." (Stack Overflow)(unix.stackexchange.com)^