Translations of this page:

Arround Zim Desktop

Zim Desktop is a great software! It allows to create and manage one or more personnal wiki-like knowledge database, diligently and easily, on any desktop computer, without the use of any cloud service (preserving data privacy), and without the need of a webserver!

This web page is written to share my experience with persons who have minimal experience with both Zim Desktop and a debian based GNU/Linux operating system.

If you do not know the free software Zim Desktop yet, you can read this excellent presentation (PDF) from its creator Jaap Karssenberg and download the program from the official site (look at the bottom of zim page).

Bringing notebooks to portable environment

Working at my custommers'homes, I am often brought to fix computers whose are running under Microsoft Windows for the greater part. In addition to my differents tools such as Live Boot CDs/DVDs/USB sticks, portable programs and Windows batch programs, I have a huge polypropylene pockets display book filled by technical specifications and procedures of my own.

But the world of computers is like the medecine one: there is a continuous multiplication of IT sub-departments and IT specification references grow in the manner of an exponential curve. So like other perfectionist persons, I feed a personal IT notebook within Zim Desktop for years.

Synchronizing my IT Zim Desktop Linux notebook to my portableApps working USB stick before each intervention, I had to open it with the ultra-basic program notepad.exe for a long time, until I find the good way to make notebooks created within Zim Desktop Linux full compatibles both with Zim Dekstop Windows portable and Zim Dekstop Linux portable.

Below, I share my experience with other GNU/Linux users in order to port their own Zim notebooks to other PCs without the need to install Zim Desktop on them.

Synchronize Zim Linux Notebooks to Zim Windows Portable

The following paragraph is about Zim Desktop Windows Portable (aka "Zim Desktop Wiki for Windows portable") with an example that locates it into the official PortableApps directory. But it can be located in any other place without the need of PortableApps plateform.

GNU/Linux users who are not familiar with rsync can look at the ubuntu related page.

Zim Windows Portable specifications

Here is a file tree summary of Zim Windows Portable with two notebooks :

ZimDesktopWikiPortable/
├── App/
├── Data/
│   ├── Config/
│   │   └── zim/
│   │       ├── accelmap
│   │       ├── notebooks.list
│   │       ├── preferences.conf
│   │       └── style.conf
│   ├── Notebooks/
│   │   ├── my-first-notebook/
│   │   │   └── notebook.zim
│   │   └── my-second-notebook/
│   │       └── notebook.zim
│   └── settings/
├── Other/
└── ZimDesktopWikiPortable.exe

Zim Desktop portability considerations

Invariant specifications used by Zim Desktop (interoperability)
  • All Zim files (configuration + notebooks ones) are always UTF-8 encoded, regardless of the operating system Zim is running on
  • All Zim configuration files always contain POSIX directory separators (slash /), regardless of the operating system Zim is running on

The page "Config files" of the official manual introduces the default content of the environment path variables XDG_CONFIG_HOME and XDG_DATA_HOME used by Zim. At the time I wrote this lines (13 sep 2015) – not having inspected zim/config/basedirs.py of source project –, thoses informations for XDG_DATA_HOME seems to me a little confused or outdated1).

Regardless of the operating system Zim is running on, let's remember the invariant specification data and meta-data storage paths are:

  • $XDG_CONFIG_HOME/zim: the directory the config files are stored into
  • $XDG_DATA_HOME/Notebooks: the default directory the notebooks files are stored into

Zim Windows portable defaults this environment variables to the following values:

  • XDG_CONFIG_HOME = ZimDesktopWikiPortable/Data/Config
  • XDG_DATA_HOME = ZimDesktopWikiPortable/Data

Limitation for Zim Windows portable:
Even though Zim works perfectly with absolute path names, nobody can know in advance the absolute path name for a USB stick! For that reason, one can store a notebook into any other directory which it is a descendant of $XDG_DATA_HOME, but it is highly inadvisable to store a notebook outside of $XDG_DATA_HOME if one wants Zim Windows portable to access it.

Porting a notebook

Each notebook is made up of a standalone directory containing a single configuration file notebook.zim wich specifies what End Of Line (EOL) type the notebook files are made of (unix, dos…). This is why all kinds of Zim versions running on all kinds of computers can access all kinds of notebooks regardless of the operation system they were created.
Also, there is nothing special to do with a rsync notebook target, except converting EOL of notebook.zim into the target operating system's one, so that it can be read.

Example of a file notebook.zim created by Zim Desktop Windows portable :

[Notebook]
version=0.4
name=MyNewWiki
interwiki=
home=Home
icon=
document_root=
shared=True
endofline=dos
disable_trash=False
profile=

For data privacy, one must set the following options when working with Zim portable on a foreign computer:

  • shared=False: make zim creates the buffer area on the USB stick rather than the hard disk
  • disable_trash=True: make zim delete notebook pages and contents without moving to the hard disk trash can

Example of a file notebook.zim that one must create if missing within a notebook directory called "MyNewWiki":

[Notebook]
version=0.4
name=MyNewWiki
interwiki=
home=MyNewWiki
icon=
document_root=
endofline=unix
profile=
shared=False
disable_trash=True
Porting the configuration files

The target file notebooks.list must be created as follow :

  • Each rsync requested notebook must have an entry and some meta-datas
  • Each notebook path must be relative to the directory $XDG_DATA_HOME (ZimDesktopWikiPortable/Data), symbolised by tilde ~. For example, the path to MyNewNoteBook witch is located into ZimDesktopWikiPortable/Data/Notebooks/ will be: ~/Notebooks/MyNewNoteBook
  • For wide compatibility the notation convention must conform zim version prior to 0.61.
    (As of Zim 0.61, header section [Notebook] is written [Notebook N] where N is an incremental number starting at 1, and in the [NotebookList] section lines begining by path such as ~/Notebooks/a_name are replaced with N=~/Notebooks/a_name where N is an incremental number starting at 1)

Example of a file notebooks.list created by Zim prior to version 0.61, and that we shall create for full compatibility:

[NotebookList]
Default=~/Notebooks/MyNewWiki
~/Notebooks/MyNewWiki

[Notebook]
uri=~/Notebooks/MyNewWiki
name=MyNewWiki
interwiki=None
icon=None

Example of a file notebooks.list created by Zim as of version 0.61:

[NotebookList]
Default=~/Notebooks/MyNewWiki
1=~/Notebooks/MyNewWiki

[Notebook 1]
uri=~/Notebooks/MyNewWiki
name=MyNewWiki
interwiki=None
icon=None

Bash function to port notebooks "made-in-linux" to Zim Windows portable

Here is a BASH (fr) function I wrote to rsync one or more notebooks created under Zim Desktop Linux to a Zim Windows Portable base directory. Feel free to modify and bring it to your scripts. It works well under debian 7 and the following package depends are required: coreutils, bash, findutils, grep, sed, rsync.

Usage:
rsync_zim_linux_to_zim_windows_portable [-v] source_dir dest_dir notebook1 [notebook2] [...] [notebookN]

Options:
-v : verbose mode to standard output

Example:
Assuming that:

  • I have 2 notebooks called work and personal in my directory $HOME/Documents/zim-database
  • My USB stick is mounted into /media/usb_stick
  • From the root of the USB stick, the Zim Windows portable directory is: PortableApps/ZimDesktopWikiPortable

All that I have to do within a BASH script is to call the function rsync_zim_to_zim_windows_portable() as following:
rsync_zim_to_zim_windows_portable -v $HOME/Documents/zim-database /media/usb_stick/PortableApps/ZimDesktopWikiPortable work personal

Here is the BASH function:

## Function to rsync one or more notebooks from linux zim to a windows ZimDesktopWikiPortable
## Note: Whatever OS is, each zim portable config file must contain UTF-8 CR/LF with UNIX slash
## Version: 1.0.1
## Debian depends: coreutils, bash, findutils, grep, sed, rsync
##
## Usage:
## rsync_zim_linux_to_zim_windows_portable [-v] source_dir dest_dir notebook_name1 [notebook_name2] [...] [notebook_nameN]
##
## Options:
## -v : verbose mode to standard output
##
##
## Example with 2 notebooks:
## rsync_zim_linux_to_zim_windows_portable -v $HOME/Documents/zim-database /media/usb_stick/PortableApps/ZimDesktopWikiPortable work personal
##
function rsync_zim_linux_to_zim_windows_portable() {
  local _VERBOSE=""
  local _OPTION
  unset OPTIND
  while getopts ":v" _OPTION; do
    case ${_OPTION} in
      v) _VERBOSE="-vvv";;
    esac
  done
  test $OPTIND -ne 1 && shift $(($OPTIND - 1))
 
  local _ERROR=0
  local _CONFIG_VER="0.4"
  local _NOTEBOOK_NR=1
  local _SRC_DIR="$1"
  local _DST_DIR="$2"
 
  shift 2
 
  test -d "${_SRC_DIR}" || return 1
  test -d "${_DST_DIR}" || return 2
 
  ## Init directories
  local _TEMP_DIR=`mktemp -d -p /tmp ${FUNCNAME}_XXXXXXXXXXXXXXXXXXXX`
  test -d "${_DST_DIR}/Data/Config/zim" || mkdir -p "${_DST_DIR}/Data/Config/zim"
  test -d "${_DST_DIR}/Data/Notebooks" || mkdir -p "${_DST_DIR}/Data/Notebooks"
 
  ## Prevent portable app warning by deleting last running path
  rm -f "${_DST_DIR}/Data/settings/ZimDesktopWikiPortableSettings.ini"
 
  ## 1/4 - General configuration files
  ## ${XDG_CONFIG_HOME:-$HOME/.config}/zim/preferences.conf -> "${_DST_DIR}/Data/Config/zim/preferences.conf"
  ## Copy the config file by converting LF to CR/LF
  test -f ${XDG_CONFIG_HOME:-$HOME/.config}/zim/preferences.conf &&
    sed 's/$/\r/' ${XDG_CONFIG_HOME:-$HOME/.config}/zim/preferences.conf >"${_DST_DIR}/Data/Config/zim/preferences.conf"
 
  ## ${XDG_CONFIG_HOME:-$HOME/.config}/zim/style.conf -> "${_DST_DIR}/Data/Config/zim/style.conf"
  ## Copy the config file by converting LF to CR/LF
  test -f ${XDG_CONFIG_HOME:-$HOME/.config}/zim/style.conf &&
    sed 's/$/\r/' ${XDG_CONFIG_HOME:-$HOME/.config}/zim/style.conf >"${_DST_DIR}/Data/Config/zim/style.conf"
 
  ## Create the header of "${_DST_DIR}/Data/Config/zim/notebooks.list"
  ## with first notebook provided in args as default one
  echo -en "[NotebookList]\r\nDefault=~/Notebooks/$1\r\n" >${_TEMP_DIR}/notebooks.list.0
 
  ## 2/4 - Remove any existing target notebook that is not in args
  find "${_DST_DIR}/Data/Notebooks" -mindepth 1 -maxdepth 1 -type d | while read _THE_DIR; do
    echo $@ | grep -Eq "\<${_THE_DIR##*/}\>" || rm -rf "${_THE_DIR}"
  done
 
  ## 3/4 - rsync each notebook provided in args
  while test -n "$1"; do
 
    test -d "${_SRC_DIR}/$1" || _ERROR=$(( 10 + ${_NOTEBOOK_NR} ))
 
    ## Stop rsync loop at first error
    test ${_ERROR} -ne 0 && break
 
    ## Add to the header of future file "${_DST_DIR}/Data/Config/zim/notebooks.list"
    echo -en "~/Notebooks/$1\r\n" >>${_TEMP_DIR}/notebooks.list.0
 
    ## Add the current notebook to the file "${_DST_DIR}/Data/Config/zim/notebooks.list"
    echo -en "[Notebook]\r\nuri=~/Notebooks/$1\r\nname=$1\r\ninterwiki=None\r\nicon=None\r\n\r\n" >>${_TEMP_DIR}/notebooks.list.${_NOTEBOOK_NR}
 
    ## rsync "${_SRC_DIR}/$1" into "${_DST_DIR}/Data/Notebooks"
    ## Note: --modify-window=1 is required for FAT target
    eval rsync ${_VERBOSE} --recursive --times --delete-after --modify-window=1 --filter=\"exclude notebook.zim\" \"${_SRC_DIR}/$1\" \"${_DST_DIR}/Data/Notebooks\"
 
    ## "${_SRC_DIR}/$1.txt" -> "${_DST_DIR}/Data/Notebooks/$1.txt"
    test -f "${_SRC_DIR}/$1.txt" && cp -u "${_SRC_DIR}/$1.txt" "${_DST_DIR}/Data/Notebooks/$1.txt"
 
    ## "${_SRC_DIR}/$1/notebook.zim" -> "${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
    if test -f "${_SRC_DIR}/$1/notebook.zim"; then
      ## Copy the config file by converting LF to CR/LF,
      ## disabling hard disk trash can and hard disk buffer
      sed 's/$/\r/' "${_SRC_DIR}/$1/notebook.zim" >"${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
      sed -i '/^\(shared\|disable_trash\)=/d' "${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
      sed -i '/^\s*$/,$h;/^\s*$/d' "${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
      echo -en "shared=False\r\ndisable_trash=True\r\n\r\n" >>"${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
    else
      ## Create the file "${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
      echo -en "[Notebook]\r\nversion=${_CONFIG_VER}\r\nname=$1\r\ninterwiki=\r\nhome=$1\r\nicon=\r\ndocument_root=\r\nendofline=unix\r\nprofile=\r\nshared=False\r\ndisable_trash=True\r\n\r\n" >"${_DST_DIR}/Data/Notebooks/$1/notebook.zim"
    fi
 
    shift
    _NOTEBOOK_NR=$(( ${_NOTEBOOK_NR} + 1 ))
 
  done
 
  ## 4/4 Create the file "${_DST_DIR}/Data/Config/zim/notebooks.list"
  echo -en "\r\n" >>${_TEMP_DIR}/notebooks.list.0
  cat ${_TEMP_DIR}/notebooks.list.* > "${_DST_DIR}/Data/Config/zim/notebooks.list"
 
  rm -rf ${_TEMP_DIR}
  return ${_ERROR}
 
}

Share notebooks from Zim windows portable to Zim linux portable

Suppose you have to work on a foreign PC running on a debian based GNU/Linux and you want to read your IT knowledge base stored within a Zim Destkop Windows portable with the use of the Zim python sources

The BASH script (fr) I have written turns an unpacked source Zim tarball into a Debian based GNU/Linux portable that works with Zim Windows portable notebooks. To do that, the following tasks are performed:

  1. Check for package depends installation
  2. Check for paths
    1. Path to the most recent python2 ELF found within /usr/bin
    2. Path to the most recent zim.py found within the script directory
    3. Path to ZimDesktopWikiPortable/Data/Config found next to the script directory
  3. Convert on the fly the config files of ZimDesktopWikiPortable to a portable linux context
    (EOL are converted from CRLF to LF. In the file notebooks.list, the notebook paths are converted to match the linux ones. In the files notebook.zim, options are set: shared=False and disable_trash=True. In the file preferences.conf, TrayIconPlugin is disabled if the standalone option is requested)
  4. Execute python 2.X with zim.py script within a special environment.
    (If the standalone option was requested and the script executed with the gui option --standalone has refused it (old zim versions), re-execute without that gui option)
  5. Wait for all processes of zim.py to terminate
  6. Convert the config files of ZimDesktopWikiPortable back to a portable Windows context
    (EOL are converted from LF to CRLF and in the file notebooks.list, the path of the notebooks are converted to match the portable Windows ones. Original files preferences.conf and notebook.zim are restored)

That script was tested on Debian 7, Devuan 1 alpha 2 and Ubuntu 14.04.
The debian depend packages are: coreutils, bash, findutils, grep, sed, gawk, python (<3), python-gtk2, python-gobject, python-xdg, xdg-utils
Feel free to use and fork it.

In addition to the technical specifications introduced in paragraph "Zim Desktop portability considerations", that script pays heed to the following Zim properties:

  • notebooks.list:
    • During startup, Zim normalizes the paths at its convenience.
      A linux portable Zim script must convert theses paths to an absolute way on the fly before executing Zim.
      The script must revert theses paths back after Zim has quit the memory
  • preferences.conf:
    • If the standalone option is requested by the user, the TrayIconPlugin must be disabled because the Zim command line gui option --standalone do not take precedence over preferences.conf
  • Memory:
    • Zim launches child processes by detaching them from its hierarchy (e.g.: openning notebooks into newer windows…).
      A linux portable Zim script must wait until all processes has ended before reverting the config files back to a Microsoft Windows portable context

Here is the typical file layout for that script to run:

root of USB stick/
├── PortableApps/
│   └── ZimDesktopWikiPortable/
└── sh/
    ├── zim-0.xx/
    └── that_script.sh

Usage assuming we call that script zim_linux_portable.sh:

zim_linux_portable.sh [-f|-g WxH+X+Y] [-h] [-s]
Options :
  -f         : start Zim portable with option '--fullscreen'
  -g WxH+X+Y : start Zim portable with option '--geometry args'
               example: -g 600x600+150+150 (default: '-g 700x500+100+100')
  -h         : print this help and exit
  -s         : start Zim Desktop Linux Portable with option '--standalone'
#!/bin/bash
SCRIPTNAME="${0##*/}"
SCRIPTDIR="$(readlink -f "${0%/*}")"
SCRIPTVERSION="1.0.0"
AUTHOR="Médéric"
RELEASE_DATE="2015"
LICENCE="GPLv3"
FRIENDLY_TASK="Launch Zim Desktop Windows Portable within a Linux context"
WELCOME="$SCRIPTNAME v.$SCRIPTVERSION by $AUTHOR ($RELEASE_DATE)"
 
## @author Médéric http://aide.ordi49.fr/wiki/contact-me.html
## Licence: GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
## THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
## APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
## HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
## OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
## THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
## PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
## IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
## ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
## Tested on Debian 7, Devuan 1 alpha 2, Ubuntu14.04
 
## Other variables
GEOMETRY_DEFAULT="--geometry 700x500+100+100"
 
## That script aims to launch a Zim linux portable program on a computer
## where zim package is not installed, in order to read the zim notebooks
## that reside within a Windows ZimDesktopWikiPortable USB stick
 
## That script was writen for debian and devuan GNU/Linux distribution,
## but it should run on any other debian based system
## Feel free to adapt and fork it at your convenience
 
## Debian depends: coreutils, bash, findutils, grep, sed, gawk,
## python (<3), python-gtk2, python-gobject, python-xdg, xdg-utils
## If one package of the second line is missing, an error is output
## telling the user that it is not installed
 
## That script can reside within a 1 level-max sub-directory of the USB stick
## (for example a subdir called sh/) or within the root of it
## The Zim linux source directory (downloaded from http://zim-wiki.org/downloads/)
## is supposed to reside within the same or a sub-directory of the script
## The ZimDesktopWikiPortable/Data/Config directory is supposed to reside
## somewhere on the USB stick
 
## Typical file layout for that script to run:
## root of USB stick/
## ├── PortableApps/
## │   └── ZimDesktopWikiPortable/
## └── sh/
##     ├── zim-0.xx/
##     └── that_script.sh
 
## How it runs:
## 1. Check for package depends installation
## 2. Check for paths :
##     - Path to the most recent python2 ELF found within /usr/bin
##     - Path to the most recent zim.py found within the script directory
##     - Path to ZimDesktopWikiPortable/Data/Config found next to the script directory
## 3. Convert on the fly the config files of ZimDesktopWikiPortable to a portable linux context
##    (EOL are converted from CRLF to LF. In the file notebooks.list,
##    the notebook paths are converted to match the linux ones.
##    In the file preferences.conf, TrayIconPlugin is disabled if the standalone option is requested.
##    In the files notebook.zim, options are set: shared=False and disable_trash=True)
## 4. Execute python 2.X with zim.py script within a special environment.
##    (If the standalone option was requested and the script executed with the gui option
##    --standalone has refused it (old zim versions), re-execute without that gui option)
## 5. Wait for all processes of zim.py to terminate
## 6. Convert the config files of ZimDesktopWikiPortable back to a portable Windows context
##    (EOL are converted from LF to CRLF and in the file notebooks.list,
##    the path of the notebooks are converted to match the portable Windows ones.
##    Original files preferences.conf and notebook.zim are restored)
 
## ____ Functions ____
function print_usage() {
  ## Depends: global variables SCRIPTNAME, GEOMETRY_DEFAULT
  echo "Usage:"
  echo "$SCRIPTNAME [-f|-g WxH+X+Y] [-h] [-s]"
  echo "Options :"
  echo "  -f         : start Zim portable with option '--fullscreen'"
  echo "  -g WxH+X+Y : start Zim portable with option '--geometry args'"
  echo "               example: -g 600x600+150+150 (default: '-g ${GEOMETRY_DEFAULT/#--geometry /}')"
  echo "  -h         : print this help and exit"
  echo "  -s         : start Zim Desktop Linux Portable with option '--standalone'"
  echo
  exit
}
 
function get_cmd_options() {
  ## Syntax:
  ## get_cmd_options $@; test $OPTIND -ne 0 && shift $(($OPTIND - 1))
  unset OPTIND
  local _OPTION
  while getopts ":fg:hs" _OPTION; do
    case ${_OPTION} in
      f) GEOMETRY="--fullscreen";;
      g) if [[ "$OPTARG" =~ ^[0-9]+x[0-9]+\+[0-9]+\+[0-9]+$ ]]; then
          GEOMETRY="--geometry $OPTARG"
        else
          echo "Wrong value for option -g. Option ignored" >&2
        fi
        ;;
      h) print_usage; exit;;
      s) STANDALONE="--standalone";;
    esac
  done
}
 
function check_zim_depends(){
  ## Based on debian package system (dpkg + APT)
  local _THE_PKG; local _MISSING=( )
 
  ## First depends: python2.X package (X>=6)
  if test -z "`/usr/bin/dpkg --get-selections | awk '$1~/^python2.[6789][0-9.]*$/{print $1}'`"; then
    test -x /usr/bin/apt-cache &&
      _MISSING[${#_MISSING[@]}]=`/usr/bin/apt-cache --names-only search python 2>/dev/null | grep -E '^python2\.[6789][0-9.]*\s' | cut -d' ' -f1 | sort | tail -n1` ||
      _MISSING[0]=python2.7
    echo "Package ${_MISSING[0]} not installed!" >&2
  fi
 
  ## Other depends
  for _THE_PKG in python-gtk2 python-gobject python-xdg xdg-utils; do
    if ! /usr/bin/dpkg -s ${_THE_PKG} 2>/dev/null | grep -Eiq 'Status:\sinstall\sok\sinstalled'; then
      echo "Package ${_THE_PKG} not installed!" >&2
      _MISSING[${#_MISSING[@]}]=${_THE_PKG}
    fi
  done
 
  return ${#_MISSING[@]}
}
 
function is_running() { ## {path/to/script/or/prog} [/path/to/a/lock/file [drop]]
  ## Return code is 0 if process is running, 1 if not
  ## 1st stage validation: is file $1 source of a process running in memory?
  test -n "`ps -ef | awk '$0!~"awk '$1'" && $0~"'$1'" {print $0}'`"
  local _RETURNCODE=$?
  ## 2e stage validation (optional):
  ## If a lockfile name $2 provided and fist test ok, test presence of $2
  if test -n "$2"; then
    if test ${_RETURNCODE} -eq 0; then
      test -f "$2" && _RETURNCODE=0 ||  _RETURNCODE=1
    fi
    ## If the optional lockfile validation fails, it is not usefull either,
    ## So if $3 say drop, delete its remains
    test ${_RETURNCODE} -eq 1 -a "$3" == "drop" && rm -f "$2" 2>/dev/null
  fi
  return ${_RETURNCODE}
}
 
function locate_python_bin() {
  echo "Searching for python program..." >&2
  local _PYTHON_BIN="`find /usr/bin -maxdepth 1 -type f -name python2* | sort | tail -n1`"
  test -n "${_PYTHON_BIN}" && echo "${_PYTHON_BIN}" || return 9
}
 
function locate_zim_py() {
  ## Search zim.py into current dir
  ## (if more than one version, get only the more recent one)
  echo "Searching for file zim.py..." >&2
  local _ZIM_PATH="`find . -type f -name zim.py 2>/dev/null | sort -V | tail -n1`"
  if test -z "${_ZIM_PATH}"; then
    echo "Not found!" >&2; return 10
  fi
  if test ! -r "${_ZIM_PATH}"; then
    if ! chmod +r "${_ZIM_PATH}" 2>/dev/null; then
      echo "Unable to read: '${_ZIM_PATH}'" >&2; return 11
    fi
  fi
  readlink -f "${_ZIM_PATH}"
}
 
function get_zim_version() { ## {path/to/python_elf} {path/to/zim_py}
  test -x "$1" -a -f "$1" || return 1
  test -f "$2" || return 2
  "$1" "$2" -v 2>/dev/null | head -n1 | grep -Eo '[0-9.]+'
}
 
function locate_zim_desktop_windows_portable_config() { ## {second_chance_base_search_dir} [first_try_path]
  echo "Searching for Zim Desktop Windows Portable config dir..." >&2
  local _ZIM_CONFIG; local _BASE_DIR="$1"
  if test -d "$2"; then
    ## Consider optionnal arg #2 is the good path
    _ZIM_CONFIG="$2"
  else
    ## Perform a deep search from the base directory given by arg #1
    test -d "${_BASE_DIR}" || _BASE_DIR=.
    _ZIM_CONFIG="`find "${_BASE_DIR}" -type d -regex "^.*/ZimDesktopWikiPortable/Data$" | head -n1`"
    if test -z "${_ZIM_CONFIG}"; then
      echo "Unable to find ${_BASE_DIR}/ZimDesktopWikiPortable/Data" >&2
      return 1
    fi
    _ZIM_CONFIG="${_ZIM_CONFIG}/Config"
    ## Config dir missing if ZimDesktopWikiPortable never run yet:
    if test ! -d "${_ZIM_CONFIG}"; then
      if ! mkdir "${_ZIM_CONFIG}" 2>/dev/null; then
        echo "Fail to create subdirectory:" >&2
        echo "${_ZIM_CONFIG}" >&2
      fi
    fi
  fi
  readlink -f "${_ZIM_CONFIG}"
}
 
function prepare_files_to_context() { ## [-s] {linux|windows} {ZimDesktopPortableConfigDir}
  local _ERROR=0
  local _CONFIG_VER="0.4"
  local _STANDALONE=false
  local _OPTION
  unset OPTIND
  while getopts ":s" _OPTION; do
    case ${_OPTION} in
      s) _STANDALONE=true;;
    esac
  done
  unset _OPTION
  test $OPTIND -ne 1 && shift $(($OPTIND - 1))
 
  test -d "$2" || return 1 && local _ZDPCD="$2" ## Zim Desktop Portable Config Dir
  local _ZDPDD="`readlink -f "${_ZDPCD}/.."`" ## Zim Desktop Portable Data Dir
  ## 1. Create zim directory if not exists yet
  if test ! -d "${_ZDPCD}/zim"; then
    if ! mkdir "${_ZDPCD}/zim" 2>/dev/null; then
      echo "Unable to create directory:" >&2
      echo "${_ZDPCD}/zim" >&2
      return 1
    fi
  fi
 
  ## 2. Create Notebooks dir if not exists yet
  if test ! -d "${_ZDPDD}/Notebooks"; then
    if ! mkdir "${_ZDPDD}/Notebooks" 2>/dev/null; then
      echo "Unable to create directory:" >&2
      echo "${_ZDPDD}/Notebooks" >&2
      return 1
    fi
  fi
 
  case $1 in
    linux)
    ## Prepare the files notebooks.list and .conf to the portable linux context
    echo "Converting config files to UNIX context..."
    ## notebooks.list
    if test -f "${_ZDPCD}/zim/notebooks.list"; then
      if file -b "${_ZDPCD}/zim/notebooks.list" | grep -q CRLF; then
        ## Convert file notebooks.list to conform linux context
        ## Backup existing notebooks.list because it looks like a windows version (CRLF)
        if ! cp -f "${_ZDPCD}/zim/notebooks.list" "${_ZDPCD}/zim/notebooks.list.winbak" 2>/dev/null; then
          echo "Fail to backup file notebooks.list" >&2
          _ERROR=1
        fi
        ## Convert relative paths of notebooks.list to absolute ones
        ## (Note: Zim 0.61+ can easily upgrade old style notebooks.list
        ## into a new one, so convert it in old style without numbered
        ## notebooks and path if it comes from zim<0.61)
        sed -i "s:^\(Default\|uri\)=~:\1=file\://${_ZDPDD}:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
        sed -i "s:^\([0-9]\+=\)*~:file\://${_ZDPDD}:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
        ## Convert new style notebook names to old style
        sed -i "s:^\[Notebook\s\+[0-9]\+\]:[Notebook]:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
      fi
    else
      ## Or create a brand new notebooks.list called Notes
      ## (Note: Zim 0.61+ can easily upgrade old style notebooks.list
      ## into a new one, so set it in old style without numbered
      ## notebooks and path if it comes from zim<0.61)
      echo -en "[NotebookList]\nDefault=file://${_ZDPDD}/Notebooks/Notes\nfile://${_ZDPDD}/Notebooks/Notes\n\n[Notebook]\nuri=file://${_ZDPDD}/Notebooks/Notes\nname=Notes\ninterwiki=None\nicon=None\n\n" >"${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
      ## If not exists, create the file "${_ZDPDD}/Notebooks/Notes/notebook.zim"
      if test ! -f "${_ZDPDD}/Notebooks/Notes/notebook.zim"; then
        test -d "${_ZDPDD}/Notebooks/Notes" || mkdir "${_ZDPDD}/Notebooks/Notes" 2>/dev/null || _ERROR=3
        echo -e "[Notebook]\nversion=${_CONFIG_VER}\nname=Notes\ninterwiki=\nhome=Notes\nicon=\ndocument_root=\nendofline=unix\nprofile=\n" >"${_ZDPDD}/Notebooks/Notes/notebook.zim" 2>/dev/null || _ERROR=3
      fi
    fi
 
    ## preferences.conf: if optional arg -s (standalone) is provided,
    ## be sure standalone mode is active: do not load trayicon plugin
    if ${_STANDALONE} && test -f "${_ZDPCD}/zim/preferences.conf"; then
      ## Backup file preferences.conf and
      if cp -f "${_ZDPCD}/zim/preferences.conf" "${_ZDPCD}/zim/preferences.conf.winbak" 2>/dev/null; then
        sed -i '/^\[TrayIconPlugin\]/,/^\s*$/d' "${_ZDPCD}/zim/preferences.conf" 2>/dev/null
        sed -i '/^\[General\]/,/^\s*$/s/"trayicon",\?//' "${_ZDPCD}/zim/preferences.conf" 2>/dev/null
        sed -i '/^\[General\]/,/^\s*$/s/,\]/]/' "${_ZDPCD}/zim/preferences.conf" 2>/dev/null
      fi
    fi
 
    ## Convert all config files from CRLF to LF
    if file -b "${_ZDPCD}/zim/notebooks.list" | grep -q CRLF; then
      sed -i 's/\x0D$//' "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=3
    fi
    find "${_ZDPCD}/zim" -type f -name '*.conf' | while read THE_FILE; do
      file -b "$THE_FILE" | grep -q CRLF && sed -i 's/\x0D$//' "$THE_FILE" 2>/dev/null || _ERROR=3
    done
 
    ## Files notebook.zim: scan notebook folders searching for notebook.zim,
    ## secure them against data-leak (shared=False + disable_trash=True) or
    ## create them if missing
    sed '/^file:/s/^file:\/\///p;d' "${_ZDPCD}/zim/notebooks.list" | while read THE_PATH; do
      if test -f "$THE_PATH"/notebook.zim; then
        ## Disable hard disk trash can and hard disk buffer
        cp -f "$THE_PATH"/notebook.zim "$THE_PATH"/notebook.zim.linbak 2>/dev/null || _ERROR=3
        sed -i '/^\(shared\|disable_trash\)=/d' "$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=3
        sed -i '/^\s*$/,$h;/^\s*$/d' "$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=3
        echo -e "shared=False\ndisable_trash=True\n" >>"$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=3
      else
        ## notebook.zim not exists: create it
        echo -e "[Notebook]\nversion=${_CONFIG_VER}\nname=${THE_PATH##*/}\ninterwiki=\nhome=${THE_PATH##*/}\nicon=\ndocument_root=\nendofline=unix\nprofile=\nshared=False\ndisable_trash=True\n" >"$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=3
      fi
    done
 
    ;;
 
    windows)
    ## Prepare the files notebooks.list and .conf to the portable windows context
    echo "Reverting config files to Microsoft Windows context..."
    ## Notebooks.list
    if test -f "${_ZDPCD}/zim/notebooks.list"; then
      if ! file -b "${_ZDPCD}/zim/notebooks.list" | grep -q CRLF; then
        ## 3.2.1.1. Convert file notebooks.list to conform windows context
        ## Backup existing notebooks.list because it looks like a linux version (not CRLF)
        cp -f "${_ZDPCD}/zim/notebooks.list" "${_ZDPCD}/zim/notebooks.list.linbak" 2>/dev/null
        ## Convert Zim linux portable paths of notebooks back to Zim Windows Portable ones
        local _RELPATH_FROM_HOME="`echo ${_ZDPDD} | sed "s:^$HOME:~:"`"
        ## (Note: Zim 0.61+ can easily upgrade old style notebooks.list
        ## into a new one, so convert it in old style without numbered
        ## notebooks and path for full compatibility)
        ## Convert relative linux paths of notebooks back to relative "windows" ones
        sed -i "s:^\(Default\|uri\)=${_RELPATH_FROM_HOME}:\1=~:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
        sed -i "s:^\([0-9]\+=\)*${_RELPATH_FROM_HOME}:~:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
        ## Convert absolute linux paths of notebooks back to relative "windows" ones
        sed -i "s:^\(Default\|uri\)=\(file\://\)*${_ZDPDD}:\1=~:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
        sed -i "s:^\([0-9]\+=\)*\(file\://\)*${_ZDPDD}:~:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
        ## Convert new style notebook names to old style
        sed -i "s:^\[Notebook\s\+[0-9]\+\]:[Notebook]:" "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
      fi
    else
      ## Or create file notebooks.list from previous Windows backup version
      ## (at that time, EOL must stay LF)
      if test -f "${_ZDPCD}/zim/notebooks.list.winbak"; then
        if ! sed 's/\x0D$//' "${_ZDPCD}/zim/notebooks.list.winbak" >"${_ZDPCD}/zim/notebooks.list" 2>/dev/null; then
          echo "Fail to restore notebooks.list from file:" >&2
          echo "${_ZDPCD}/zim/notebooks.list.winbak" >&2
          _ERROR=5
        fi
      fi
    fi
    ## If optional arg -s (standalone) is provided and
    ## if preferences.conf was previously backup (for standalone option), restore it
    if ${_STANDALONE} && test -f "${_ZDPCD}/zim/preferences.conf.winbak"; then
      test -f "${_ZDPCD}/zim/preferences.conf" &&
        cp -f "${_ZDPCD}/zim/preferences.conf" "${_ZDPCD}/zim/preferences.conf.linbak" 2>/dev/null
      mv -f "${_ZDPCD}/zim/preferences.conf.winbak" "${_ZDPCD}/zim/preferences.conf" 2>/dev/null
    fi
 
    if test -f "${_ZDPCD}/zim/notebooks.list"; then
      ## Files notebook.zim: scan notebook folders searching for notebook.zim.linbak
      grep -E '^(file:|~)' "${_ZDPCD}/zim/notebooks.list" | sed -e 's/^file:\/\///' -e "s:^\([0-9]\+=\)*~:${_ZDPDD}:" | while read THE_PATH; do
        if test -f "$THE_PATH"/notebook.zim.linbak; then
          ## Restore previous version to revert secured options (shared= + disable_trash=)
          cp -f "$THE_PATH"/notebook.zim.linbak "$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=4
        else
          ## notebook.zim not exists: create it
          echo -e "[Notebook]\nversion=${_CONFIG_VER}\nname=${THE_PATH##*/}\ninterwiki=\nhome=${THE_PATH##*/}\nicon=\ndocument_root=\nendofline=unix\nprofile=\nshared=False\ndisable_trash=True\n" >"$THE_PATH"/notebook.zim 2>/dev/null || _ERROR=4
        fi
      done
    fi
 
    ## Convert all config files from LF to CRLF
    if ! file -b "${_ZDPCD}/zim/notebooks.list" | grep -q CRLF; then
      sed -i 's/$/\r/' "${_ZDPCD}/zim/notebooks.list" 2>/dev/null || _ERROR=4
    fi
    find "${_ZDPCD}/zim" -type f -name '*.conf' | while read THE_FILE; do
      if ! file -b "$THE_FILE" | grep -q CRLF; then
        sed -i 's/$/\r/' "$THE_FILE" 2>/dev/null || _ERROR=4
      fi
    done
 
    ;;
 
    *) echo "Config files conversion type not defined" >&2
    return 5
    ;;
 
  esac
  return ${_ERROR}
}
 
## _____ Main _____
echo $WELCOME
echo $FRIENDLY_TASK
echo
cd "$SCRIPTDIR"
 
## 1. Analyse command line options
get_cmd_options $@
test $OPTIND -ne 0 && shift $(($OPTIND - 1))
test -z "$GEOMETRY" && GEOMETRY="$GEOMETRY_DEFAULT"
test -n "$STANDALONE" && STANDALONE_SHORT="-s" || unset STANDALONE_SHORT
 
## 2. Be sure that all depends are installed
check_zim_depends || exit $?
 
## 3. Locate the python2 ELF
PYTHON_BIN="`locate_python_bin`" || exit 9
 
## 4. Locate zim.py file
ZIM_PY="`locate_zim_py`" || exit 10
ZIM_VERSION="`get_zim_version $PYTHON_BIN "$ZIM_PY"`" || exit 11
echo "Found: Zim Desktop v.$ZIM_VERSION"
 
## 5. Locate ZimDesktopWikiPortable/Data/Config and set XDG_CONFIG_HOME
## (Note: XDG_CONFIG_HOME should not be equal to $HOME/.config
## because we are not interesting by the personal notebook(s) of the user)
## The script is supposed to be in a subdir of the USB stick (for example sh/),
## and we're not sure Zim Desktop Windows Portable reside into it's native dir.
## So: performs a 2 chances search to find its config path
XDG_CONFIG_HOME="`locate_zim_desktop_windows_portable_config .. "../PortableApps/ZimDesktopWikiPortable/Data/Config"`" || exit 12
 
## 6. Check for safety operations...
## Problem:
## If another instance is run from "$ZIM_PY", maybe it is from that
## script, or maybe not (direct mouse clic on zim.py) and in that case
## other config files should be used.
## Before to continue, we must be sure that no other instance of zim
## and of that script will modify our portable config files (this could
## lead to ruin them).
## Unfortunately:
## 1. Zim closes config files as soon as read and we cannot lsof them
## 2. Instances of /usr/bin/python2.x is detached (PPID=1) when
##    it is executed from Zim (File > Open Another Notebook)
##    it is executed from a Windows manager (tested with Openbox)
## So, it's impossible to strickly check (even with a lockfile) and the
## only solution is to exclude any other instance of $ZIM_PY in memory:
if is_running "$ZIM_PY"; then
  ## Alert the user, and stop the script
  echo
  echo "Zim instance found in memory from the following zim program:"
  echo "$ZIM_PY"
  echo "Please, close that instance before running $SCRIPTNAME again."
  exit 0
fi
 
## 7. Convert config files from CRLF to LF and prepare
##    the files notebooks.list, notebook.zim and .conf to conform linux context
prepare_files_to_context $STANDALONE_SHORT linux "${XDG_CONFIG_HOME}" || exit 13
 
## 8. Launch Zim Desktop Linux portable
ZIM_STDERR=/tmp/${SCRIPTNAME// /}-${RANDOM}${RANDOM}
echo "Launching Zim Desktop Linux portable $ZIM_VERSION..."
echo "Do not close this terminal window while Zim is in memory"
echo
## Execute Zim with special environment
## Note: Zim 0.63 ignore option --geometry if no other gui option is provided (minor bug?)
env XDG_CONFIG_HOME="$XDG_CONFIG_HOME" $PYTHON_BIN "$ZIM_PY" --gui $STANDALONE $GEOMETRY 2>$ZIM_STDERR
## If standalone error (old versions of zim), try without that option
if test -n "$STANDALONE" -a -s $ZIM_STDERR && grep -qE "option\s+--standalone\s+not\s+recognized" $ZIM_STDERR; then
  env XDG_CONFIG_HOME="$XDG_CONFIG_HOME" $PYTHON_BIN "$ZIM_PY" --gui $GEOMETRY 2>$ZIM_STDERR
fi
## Print zim stdout
if test -s $ZIM_STDERR; then
  echo "Zim error messages:" >&2
  cat $ZIM_STDERR >&2
fi
rm -f $ZIM_STDERR
 
## 9. Wait until all instances of zim has quit memory
## (New instances of zim (others notebooks) are detached from the parent
## (=the first instance). So, if ones closes the first instance,
## the parent script continues but other instances remains in memory)
while is_running "$ZIM_PY"; do
  if test ${ECHOED:-0} -eq 0; then
    echo
    echo "Waiting for last instance of zim to end..."
    ECHOED=1
  fi
  sleep 1
done
 
## 10. Reverse config files format converting LF to CRLF
##    and preparing them to conform to Zim Desktop Windows Portable
echo
prepare_files_to_context $STANDALONE_SHORT windows "${XDG_CONFIG_HOME}"
ERROR=$?
case $ERROR in
  0) echo "All things done.";;
  4) echo "An error occurs in configuration files!"
  echo "Please, check file notebooks.list before runing this prog again";;
esac
echo
exit $ERROR

Download that script

References

1)
I noticed that with Zim Desktop Linux 0.56 running under Debian 7/LXDE:
  • Zim config files are created in ${XDG_CONFIG_HOME:-$HOME/.config}/zim
  • New notebooks are created by default into $HOME/Notebooks when the environment XDG_DATA_HOME is emtpy.
    So we can conclude that Zim defaults XDG_DATA_HOME variable to ${XDG_DATA_HOME:-$HOME} but not to $HOME/.local/share as it is written on the official config files help page
With Zim Desktop Windows 0.63:
  • Zim config files are created into %APPDATA%\zim\config\zim. This conforms to the official config files help page where XDG_CONFIG_HOME defaults to APPDATA/zim/config
  • XDG_DATA_HOME is supposed to default to APPDATA/zim/data but Zim Desktop Windows 0.63 stores New notebooks by default into %USERPROFILE%\Notebooks.
    So we can conclude that Zim defaults the environment variable XDG_DATA_HOME to %USERPROFILE% but not to APPDATA/zim/data as it is written on the official config files help page