Human readable timestamp for filenames

I use this bash alias (should work in Linux, Cygwin, probably MacOS, maybe other Unixes):

alias timestamp="date --rfc-3339=ns | tr ' '  '_'"

Then use it like this for example to archive a file:

mv somefile somefile_$(timestamp)

You should see something like

`somefile' -> `somefile_2014-11-13_11:45:46.980175800-04:00'

If you don’t like colons in file names, change tr ' ' to tr ' :'.

Start script for stand-alone Java process

Directory structure

  • lib
    • some.jar
    • another.jar
  • config
    • dev
    • uat
  • start.sh
  • .setenv.sh

.setenv.sh

#!/bin/bash 

# environment (typically 'dev', 'uat' or 'prod') 
env=

# absolute path to java home ( >= 1.7 ) 
# such that $java_home/bin/java exists 
java_home=${JAVA_HOME} 

# add any additional environment setup here:

start.sh

#!/bin/bash 

# script should exit if any command fails 
set -e 

function envfail() { 
  echo $1; 
  echo "Please adjust $setenv_script and retry ..." 
  exit 1 
} 
function check_not_empty() { 
  if [ -z "$1" ]; then 
    envfail "Variable '$2' is unset or empty." 
  fi 
} 

script=$(readlink -f "$0") 
if [[ "$script" =~ .*[[:space:]].* ]]; then 
  echo "Script path contains spaces: $script" 
  echo "Please fix and retry ..." 
  exit 1 
fi 

# ok, now we know $script contains no spaces so 
# we don't need quoting acrobatics from here on 

dir=$(dirname $script) 
parent=$(dirname $dir) 
setenv_script=$parent/setenv.sh 

if [ ! -e $setenv_script ]; then 
  echo "$setenv_script not found" 
  cp $dir/.setenv.sh $setenv_script 
  envfail "Created a default $setenv_script for you." 
fi 
setenv_script_file=$(readlink -f $setenv_script) 
if [ -r $setenv_script_file ]; then 
  echo "Sourcing $setenv_script" 
  source $setenv_script 
else 
  envfail "Cannot read $setenv_script" 
fi 

check_not_empty "$env" 'env' 
config_env=$(readlink -f "$dir/config/$env") 
if [[ ! -r "$config_env" || ! -d "$config_env" ]]; then 
  envfail "Invalid env=$env : Cannot read directory $config_env" 
fi 

check_not_empty "$java_home" 'java_home' 
java="$java_home/bin/java" 
if [ ! -e "$java" ]; then 
  envfail "$java is not an executable file" 
fi 
java_version=$("$java" -version 2>&1 \
               | head -n 1 | cut -d'"' -f2 | cut -d'.' -f2) 
if [ $java_version -lt "7" ]; then 
  envfail "Java 1.7 or higher is required." 
fi 

# turn on bash debug output for the following lines 
set -x 

cd $dir 
mkdir -p "../log" 

nohup \ 
"$java" -cp "$config_env:lib/*" \
        net.doepner.example.Main \ 
        > "../log/example_stdout.log" 2>&1 & 

Get java version string via shell commands

Determine the pure java version string from any Unix/Linux shell (including Cygwin):

java -version 2>&1 | head -n 1 | cut -d'"' -f2

This requires only the very commonly available and lightweight “head” and “cut” commands.

I originally found the one-liner on stackoverflow. Thanks to the friendly folks who shared it.

To get only the major version part (e.g. 8 for Java 1.8.x, 11 for 11.x), use this:

java -version 2>&1 \
  | head -1 \
  | cut -d'"' -f2 \
  | sed 's/^1\.//' \
  | cut -d'.' -f1

Note: The sed step is required for versions up to Java 8 that start with the “1.” prefix.

Example: Ensure Java 11 or higher:

#!/bin/bash

version=$(java -version 2>&1 \
  | head -1 \
  | cut -d'"' -f2 \
  | sed 's/^1\.//' \
  | cut -d'.' -f1 
)

if [ $version -lt "11" ]; then
  echo "Java 11 or higher is required."
  exit 1
fi

A minttyrc for black on white with red block cursor

I don’t particularly like the tradition of light text on dark backgrounds in everything that (shell) coders or Unix admins seem to use. But even the Jetbrains folks provide a Darcula theme now.

Anyway, I want more light in my mintty – which is the default Cygwin terminal – and an easy to find cursor:

oliver@windowsbox ~
$ cat .minttyrc 

BoldAsFont=yes
BoldAsColour=yes
BackgroundColour=240,220,180
ForegroundColour=0,0,0
CursorColour=255,0,0
CursorType=block
Black=0,0,0
BoldBlack=0,0,0
Red=160,0,0
BoldRed=80,0,0
Green=0,160,0
BoldGreen=0,80,0
Yellow=80,80,0
BoldYellow=40,40,0
Blue=0,0,160
BoldBlue=0,0,80
Magenta=100,0,60
BoldMagenta=50,0,30
Cyan=60,0,100
BoldCyan=30,0,50
White=60,60,60
BoldWhite=40,40,40
Font=Lucida Console
FontHeight=10

If you come across other colors that are too light to be readable on white background, just add more ColorName=0,0,0 lines in this file. Make sure you consult the related mintty reference documentation.

For existing work on more beautiful(?) but IMHO less ergonomic palettes of colors, you might want to check out these “Solarized” color configs for mintty.

XFCE 4.8 panel : Fix missing Suspend and Hibernate icons

The “Action Buttons” panel plugin of XFCE 4.8 on Debian “wheezy” has a known bug regarding missing icons for the Suspend and Hibernate actions and users will only see a generic placeholder icon.

Ognyan Kulev who reported the bug says this is because most icon themes do not provide icons named “system-suspend” and “system-hibernate”.

As a work-around he suggests linking to the corresponding xfce4-power-manager icons. This fixed the issue on my Debian laptop:

sudo apt-get install xfce4-power-manager-data
cd /usr/share/icons/hicolor/scalable/actions
sudo ln -s xfpm-suspend.svg system-suspend.svg
sudo ln -s xfpm-hibernate.svg system-hibernate.svg
sudo gtk-update-icon-cache-3.0 -f ../..

I saved save the commands into a shell script so I can run it again if the symbolic links get deleted during an apt-get update or accidental apt-get remove:

echo "#! /bin/sh" > /usr/local/bin/fix-xfce-action-icons.sh
chmod ugo+x /usr/local/bin/fix-xfce-action-icons.sh
sudo vim /usr/local/bin/fix-xfce-action-icons.sh

Organize image and video files by creation date with exiftool

I use a simple bash script /usr/local/bin/move-cam-files.sh to let exiftool move photos and videos from our camera to directories on our file server, based on creation date as per media file metadata:

#! /bin/bash -x

move() {
  exiftool -r -v -ext $2 '-directory<DateTimeOriginal' \
           -d "/home/storage/$3/%Y/%m" "$1"
}

# directory where to read media files from is $1
# if unspecified use current directory
dir=${1:-.}

move $dir AVI videos
move $dir JPG photos

Install and maintain Cygwin without Windows admin rights

Sometimes you work on Windows as a restricted user, without admin rights. Like many other good software packages, Cygwin can be installed anyway (see the respective FAQ entry).

These are the steps I used:

  1. Download setup-x86.exe (32bit) or setup-x86_64.exe (64bit).
  2. Run it with the “--no-admin” option
  3. During installation select the “wget” package
  4. Create /usr/local/bin/cygwin-setup.sh (for 32bit omit the “_64”):
    #! /bin/sh
    rm setup-x86_64.exe
    wget http://cygwin.com/setup-x86_64.exe
    chmod u+x setup-x86_64.exe
    run ./setup-x86_64.exe --no-admin
    
  5. Make the script executable:
    chmod ugo+x /usr/local/bin/cygwin-setup.sh
  6. Create a copy of the Cygwin terminal shortcut, rename it “Cygwin setup”
  7. Edit the shortcut target, replace
    mintty.exe -i /Cygwin-Terminal.ico -

    with

    mintty.exe -i /Cygwin-Terminal.ico /bin/bash -l -c 'cygwin-setup.sh'

Whenever you want to run the Cygwin installer to install or remove packages, you can just execute the shortcut or run cygwin-setup.sh from the Cygwin command prompt.

Alternatively, you could also use the pure command-line tool apt-cyg.

Use standard bash for ssh login on shared hosts

Please note: The following instructions are for ssh logins on a remote host. The approach is not suitable for executing remote commands via ssh.

Problem

When you work on a remote Linux or Unix server (via ssh) you sometimes cannot control your login shell and/or its default config file. For example, you might be sharing the same user account on the server with other people or the use of the chsh tool might be locked down.

Maybe the default shell is something like ksh, or if bash is used maybe the .bashrc sets vi key bindings. This can be annoying if you are used to standard Linux bash with its default Emacs style bindings.

Suggested solution

In these cases you can do the following, assuming bash is installed and in the path on the host:

1) Create /usr/local/bin/sbash.sh (on Windows, use Cygwin).:

#! /bin/bash

 set -x

 if [ "$#" -gt 0 ]; then
   user_at_host="$1"
   shift
   ssh_options="$@"
 else
   set +x
   echo Usage: $(basename "$0") user@host [ssh-options]
   exit 1
 fi

 ssh -t $ssh_options \
        $user_at_host \
     "bash --rcfile .bashrc.for-remote-user-${USER}"

Make the file executable using something like chmod ugo+x /usr/local/bin/sbash.sh. You can then use it for remote logins like the ssh command, for example:

 
oliver@basement:~$ sbash.sh user@host

The $USER variable in the sbash.sh script will be substituted by the local shell with your local user name, which in this example is “oliver”.

2) On the host create ~/.bashrc.for_remote_user_USERNAME where USERNAME is the user name from the ssh client as mentioned above:

user@host$ vim $HOME/.bashrc.for-remote-user-oliver

Make sure this file name matches the –rcfile option in your ssh command.

You can then edit and use this file like a normal .bashrc file, i.e. for setting your favorite environment variables, bash options, aliases, etc.

Nightly file server backups to external harddrive

I use a small headless Debian system as file server for all family photos, videos, documents, etc. Its hostname is “bubba”. I have recently set it up to run backups to an external harddrive, using cron and rsync.

The external disk is a 500G laptop SATA disk in an USB/eSATA enclosure. It requires no separate power supply. So far I have only got it to work over USB. Somehow the eSATA does not work for me on Debian 6 (aka “squeeze”), even though the file server has an eSATA port.

Prerequisites


sudo mkdir /mnt/backup
sudo apt-get install ntfs-3g

NTFS mount/unmount with sudo

I use NTFS as the filesystem on the backup disk because we wanted it to be compatible with MS Windows. The Debian Linux on the file server uses ntfs-3g for mounting the disk read-write. Unfortunately that only works well with root rights, so I configured sudo to permit myself password-less mounting and unmounting of the device.

/etc/sudoers entry
oliver ALL = NOPASSWD: /bin/mount /mnt/backup, \
                       /bin/umount /mnt/backup

Nightly rsync

The nightly backup process itself is a simple non-destructive local rsync command, wrapped by mount and unmount commands, to make sure that we can unplug the external disk anytime we want (just not around midnight).

My crontab:


oliver@bubba:~$ crontab -l
0 0 * * * /home/oliver/shared/scripts/backup.sh

The backup.sh script
#! /bin/sh

if mountpoint /mnt/backup; then
  sudo umount /mnt/backup
fi

sudo mount /mnt/backup

if [ $? -eq 0 ]; then

  rsync -avvih --progress \
    --exclude /downloads \
    --exclude /movies \
  /home/storage/ /home/oliver/backup \
  > /tmp/cron_output.log 2>&1

fi

sudo umount /mnt/backup

Symlinks and fstab

Symlink in my home for convenience:

oliver@bubba:~$ ls -l /home/oliver/backup
lrwxrwxrwx 1 oliver users 11 Aug 7 21:38 /home/oliver/backup -> /mnt/backup/

Entry in /etc/fstab:

oliver@bubba:~$ grep "/mnt/backup" /etc/fstab
/usr/local/share/backup /mnt/backup ntfs-3g defaults 0 0

Device symlink /usr/local/share/backup:

oliver@bubba:~$ ls -l /usr/local/share/backup
lrwxrwxrwx 1 root staff 84 Jun 23 01:51 /usr/local/share/backup -> /dev/disk/by-id/usb-WDC_WD50_00BPVT-00HXZT3_FDC0FD500000000FD0FF61A6103926-0:0-part1

Try it manually


/home/oliver/shared/scripts/backup.sh &
less /tmp/cron_output.log

Room for improvement

The symlink to the device file is the ugliest part of the whole solution. Currently I have to plug the disk directly into a USB slot on the file server because if I connect it via a USB hub, it will appear under a different name in /dev/disk/by-id and my symlink won’t work. I would like to use a udev rule instead that automatically creates an identical symlink no matter how the the disk is plugged in.

I would also like to implement a 2-way backup so that files we put on the external disk, for example photos from a trip to relatives, will be mirrored to the file server. It should be just another rsync command going in the opposite reaction.

Maybe I would also like the backup process to start right away when the disk is plugged in, in addition to the nightly cron job. This would probably require another udev rule.