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.

Sqoop daily Oracle data into Hive table partition

The following bash script can be used to import Oracle records into a Hive table, partitioned by date. It uses Sqoop. Both Hive and Sqoop are part of typical Hadoop distributions, like the Hortonworks Sandbox, for example.

#!/bin/sh

function upper() {
  echo "$1" | tr [a-z] [A-Z]
}

if [ $# -ge 5 ]; then
  schema=$(upper $1)
  table=$(upper $2)
  column_to_split_by=$(upper $3)
  date_column=$(upper $4)
  date_value="$5"
else 
  echo
  echo "Usage: $(basename $0) schema table column-to-split-by date-column YYYY-MM-DD"
  echo
  echo "Imports all records where value of date-column is \$date_value from"
  echo "Oracle table \$schema.\$table as a Hive table partition."
  echo "Hadoop will split the import job based on the column-to-split-by."
  echo "* The table must have the columns specified as column-to-split-by and date-column."
  echo "* The column-to-split-by must be finer granularity than date-column, ideally unique."
  echo "* The date_value must be in YYYY-MM-DD format."
  echo "* If date_value is unspecified, the current date will be used."
  exit 1
fi

echo "schema = $schema"
echo "table = $table"
echo "column_to_split_by = $column_to_split_by"
echo "date_column = $date_column"
echo "date_value = $date_value"

# we have to drop the partition, because --hive-overwrite does not seem to do it
hive -e "use $schema; alter table $table drop if exists partition($date_column='$date_value');"

columns=$( \
sqoop eval \
--options-file /usr/local/etc/sqoop-options.txt \
--query "select column_name from all_tab_columns where table_name = '$table'" \
| tr -d " |" \
| grep -Ev "\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-|COLUMN_NAME|$date_column" \
| tr '\n' ',' \
| sed -e 's/\,$//'
)

query="select $columns from $schema.$table \
       where $date_column = to_date('$date_value', 'YYYY-MM-DD') \
       and \$CONDITIONS"

echo "query = $query"

sqoop import \
--options-file "/usr/local/etc/sqoop-options.txt" \
--query "$query" \
--split-by "$column_to_split_by" \
--target-dir "$schema.$table" \
--hive-import \
--hive-overwrite \
--hive-table "$schema.$table" \
--hive-partition-key "$date_column" \
--hive-partition-value "$date_value" \
--outdir $HOME/java

JDBC connection details

Put them into /usr/local/etc/sqoop-options.txt, in a format like this:

--connect
jdbc:oracle:thin:@hostname:port:hostname
--username
oracle_username
--password
oracle_password

Reflexionen der Moderne im dramatischen Werk Ernst Tollers

About 12 years ago, in July 2001, I submitted my thesis “Zwischen Weltverbesserung und Isolation – Reflexionen der Moderne im dramatischen Werk Ernst Tollers” to complete my university degree in Mathematics and German Linguistics and Literature.

I wrote the document using Latex and GNU Emacs on a GNU/Linux system. It is available in PDF format.

The LaTex source files of the thesis are also available. The structure is very straightforward and uses predefined macro definitions that can be generally useful for writing essays, books and academic papers in the Liberal Arts.

For those who are fed up with their word processor messing with their layouts and prefer to just write plain text: Take a look at how simple, for example my introduction chapter is.

If you are interested, feel free to reuse my LaTeX macros in STYLE.tex.

The LaTeX code is compatible with TeX Live, version 2012. On Debian stable (wheezy) installation is as simple as

sudo apt-get install texlive texlive-latex-extra texlive-lang-german evince
wget https://github.com/odoepner/toller-moderne/archive/master.zip
unzip master.zip
cd toller-moderne-master/src/main/tex
pdflatex MAIN.tex
evince MAIN.pdf