musicmatzes blog

bash

This article is about the annoyance of programming language tutorials. First, I will argue why learning new programming languages always feels a bit like a pain to me. And then I will tell you why learning Rust was different.

I know a few programming languages. I've done things in C, Ruby, Java, Python, PHP, C++, Bash, Lisp (Racket, actually), Haskell, LaTeX, Nix, Make, Lua* and JavaScript*.

*: Only a bit, but still enough to say “I've done things in this language”

When I started learning programming, I learned Java and shortly after that Ruby. Both languages have their oddities, but besides their overall concept (imperative programming and object orientated programming) they are both rather easy to learn. After that I learned C. Learning C was a new world for me back then, as I only knew object orientation. I cannot remember which resources I've been using learning C, though I remember it was a pain. I had a good friend helping me, which was awesome, though I struggled a lot with the new concept.

After learning C, I did a fair amount of PHP. Learning PHP was simple (I actually did it in one weekend and a few thousand lines), though it again was a pain. It was a pain because of the tutorials and resources. And that's the point I want to discuss here.

All other programming languages I've been learning so far were painful to learn. Not because of their concepts (see Haskell, functional programming at all), but because of the resources. Haskell is a really good example here. There is exactly one resource I enjoyed while learning Haskell, and that's “Learn you a Haskell for great good”. And why? Because it takes your hand and shows you the new world. It is good because the concept of functional programming is new for most people reading this resource. Learning JavaScript, Bash, Lua or Python sucks because you already know what an object is. You know what a function or method is and you know that the basic syntax for arithmetic is the very same as in the languages you've been using by now. But all tutorials expect you start from zero – which is fine if you're actually starting from zero, but most of the time you're not. After learning Java and Ruby I understood what OOP is (actually not after learning Java, but after learning Ruby, because Java isn't a fully object oriented language in my opinion).

So, learning Haskell was kinda amazing. Learn you a Haskell is a really great book and if you want to learn Haskell, you really should read it. But what was even a better experience was learning Rust.

So why is that? It is because Rust is a new concept and the old concept (imperative programming). Learning Rust is not like learning Python after learning Ruby. It is like Learning Haskell after learning Racket. The concept behind the language is kinda the same, though it is different in most details.

Plus, the Rust tutorials and resources are freakin' awesome. Let me repeat that. The tutorials and documentation of the tools, libraries and everything, the resources on how to do things, are awesome!

Every documentation for each library looks exactly the same, which is a huge advantage over other languages library documentations. cargo, the Rust build-tool builds the documentation locally for you, if you want that. So working offline after you fetched the dependencies or your project (also via cargo) is no hassle. Building a new project with cargo is absolutely painless, its literally just executing a command, editing a textfile and executing another command (setting up the project, writing down dependencies and meta information about the project and building the project, which fetches the dependencies).

So really, if you want to learn a new programming language, check out Rust. I haven't even talked about its language features, and I will not, as this is not the goal of this article. This article is about resources and tutorials. And my conclusion is:

All programming language tutorials suck. Except the Rust language tutorial. tags: #programming #open source #bash #c #c++ #haskell #nix #racket #rust #shell

Everyone starts using shell environments, where custom variables are available, custom bash functions are enabled, special commands are available, maybe even some additional programs are installed.

Projects on github pop up for handling these things, installing bash “environments” and so on. And I have to ask: Why?

I tell you, there's a solution to all of your problems. It is called “nix”. It is a package manager which provides you opportunities, you've never dreamed of!

Yes, you could hack the functionality of these project-environment-manager things yourself (that's what people did, then they put it on github and now you see it and decide to write your own becaues you can do it better). You could use this piece of clusterbash and suffer from impurities you'd never expect. Or, you could just use the purity of the nix package manager to deploy the world in a shell.

With the nix package manager, you can use custom project-based environments to install packages neither globally nor in your user package environment:

nix-shell -p haskellPackages.pandoc

for example installs “Pandoc” and starts a shell where it is available. The executable is not available anymore after you type exit. It is not installed in your system, it is not even installed in you user package set. It was available in this shell and only will be available again after you entered this shell via nix-shell.

I have a lot of packages...

... so have I. Normally, I install one or two packages via commandline in my nix-shell environment. If there are more packages or I need the packages on multiple systems (when developing something, for example), I add a default.nix file to the repository:

{ stdenv, pkgs ? (import <nixpkgs> {}) }:

let
  env = with pkgs; [ racket ];
in

stdenv.mkDerivation rec {
    name        = "blog";
    src         = ./.;
    version     = "0.0.0";
    buildInputs = [ env ];
}

The I only have to type nix-shell to enter the environment.

Another idea would be to install the packages “persistent”, so after a call to nix-collect-garbage won't remove the packages from the ominous store where the packages live:

nix-instantiate . --indirect --add-root $PWD/shell.drv
nix-shell $PWD/shell.drv

(copy-pasted from the nixos-wiki)

tags: #bash #nix #nixos #programming #software

Some of you might know my little project rate. It is a bash script to collect a database of things you want to rate.

What this means and why I started to reimplement in C++ – read it here!

So I started to write this application because I wanted to keep track of animes I already watched, Bands I like but do not listen to that often and so on and so forth. What the application does is really simple: You can enter notes about a certain thing (in your favourite text editor, which is vim for me) and attach tags to it and put it into a category. Then you can give it a rating. The rating can be everything, from simply numbers to text to whatever you like. But it is supposed to be a number between 1 and 10, of course. That's also the way I use it.

So how does it work? Well, that's really simple: The “rate” script creates a directory structure in ~/.rate/collections/, whereas a folder is a “collection” of things and a file is a rating. So, I have something like “movies/animes/” as collections (they are nested, of course) and then a file for each anime I watched. The filename is generated from the title I passed “rate” when creating the entry. The file also contains a “header”, which includes a unique ID (which you really should not change), a list of tags, the title (the filename is the title, too, but as on linux you shouldn't include whitespace in file names, it is not the title as you typed it) and a rating of course. Custom fields could be added to the header, but this feature is not implemented yet. After the header, you can put your description.

That's basically it. “rate” provides some features to search this database, for example you can grep for text in your entries, you can view your entries, you can list your entries and you can attach more text to your entries or edit them. As everything is git controlled in the ~/.rate directory, you can reset if you (or the script,...) messed up somehow.

There's no release by now, as it lacks of some features by now. For example I want to be able to search for entries with a certain rating or within a certain range of ratings. I also want to be able to search for entries with aspecial set of tags or within a certain collection (I can do this, though) special set of tags or within a certain collection (I can do this, already)! After these things are implemented, I will release my first version, which would be 1.0 then. And, of course, I will package it for NixOS!

So why C++

Well, I have to learn C++ for my praxis semester. That's basically it. I wanted to learn a new programming language for a relatively long time, and that's my approach on learning C++. I was able to implement some things, but not yet to run an application. My first step would be to parse my already existing entries. Hope that works. As I stated in the github repos README.md file, I do not want to use any frameworks for this. No Boost, no Qt, no other libraries. I just want to use plain C++11 (or maybe 14 in future, not sure about this, though).

Also, C++ gives me way more posibilities when it comes to features. I can refactor things really easy and maybe the C++ implementation will continue to live whereas the bash implementation will die at some point. Who knows? The point is, the bash implementation implements a file format which is

  1. really simple
  2. forward compatible

The file actually looks like this:

key = value
other = value
another = another
---

<text>

and that's basically it. The three dashes mark the end of the header and everything before the header is a <key> = <value> list, whereas each line is only allowed to contain one key-value-pair. The value can be a list of values, for example a list of Tags whereas tags are seperated by a whitespace character (space) or a string, such as the title is. The bash implementation “knows” which key has to be which value and the C++ implementation has to know this, too, of course. But that's it.

The commandline interface of the application needs some work, though. Maybe it will get some REPL-like interface later on, or maybe something more interactive. By now, it is simply an interface where you can do one thing per call. I have no problem with that and I'm planning to keep backward compatibility with the bash implementation even if this one is not even released! It is fun to write such a tool and I really like doing it.


Also, it is not a that complex thing to implement and that's a huge point to me. I don't want to spend months and months in implementing a thing which only one or two people on this planet actually use. For bigger projects, I contribute to open source projects. That's a better way to produce large-scale software, IMHO.

tags: #bash #programming #c++ #linux #shell #vim

I started a fork of a diary application which is written in bash, because I thought there are some things missing and some features could be added – and the repository of the original author was really old.

I started to pretty it up and implement some 21st-century things so it becomes a really nice note-taking application.

So, initially the application was called “diary” and developed on sourceforge, by Jamie Briggs. Someone created a fork, named “diary-f”, which added some features.

The lastest change in the original repository is from February 2013, which is almost two years ago by now. The last change in the repository of the fork is from three years ago – even longer, yes!

So, the application per se is really good one. It is just a small shell script, but it works really good for me. Anyways, I missed some features, namingly:

  • Multiple separate entries per day
  • Everything should be kept in git, so I know when I edited the articles
  • GPG encryption everywhere

I started implementing multi-file support directly in the “diary-f” repository, but then decided to create a fork, as it won't be compatible with the original software anymore. By now, three things are done in the repository: I refactored the codebase to fit my coding style (well, it is still bash, isn't it?), mutli-file support as said and git support. I want to implement gpg support next and maybe the ability to add attachements to entries. And after these things are implemented, I want to be able to search the entry repository for certain entries in a simple way, which could be difficult, given the fact that everything is encrypted in there.


If you're interested in the application, I'd welcome pull requests for almost everything. Contributing guidelines are:

  • Document your work, how do things work?
  • KISS
  • DRY

That's basically it. I'm looking forward to your patches! tags: #bash #git #open source #programming #shell #tools

I have a problem. I want to write a small application. It should be a commandline application, but maybe I want to extend it later with a GUI or even a web interface. But I don't know what language to write it in.

This article could also be named “Which is the right tool for the job?”

The idea is simple: I want to write a journal-application, where one can take notes, tag them, query the journal for entries, optionally provide filters before printing them to print only some of them and so on.

I want to be able to encrypt them with gpg and I want to write posts to the journal with vim. I want to be able to configure these steps, of course, so I can use a certain key for encrypting the journal or maybe another editor (for these emacs lovers out there).

It should be designed to be extensible, so I can add a GUI or web interface later on. Or maybe a tex output, so I can compile a PDF out of it or something. Posts should be written in standard markdown, so this wouldn't be a big deal.

But I don't know what language to write it in. The languages which I can or would choose from are:

  • C
  • C++
  • Haskell
  • Rust
  • Java (not really...)
  • Scala (not really either, it's still JVM)
  • Ruby
  • Python (by extending jrnl)
  • Racket
  • Lua
  • Bash (I really don't want to write it in bash, but it's still a possibility)

As you can see, the first four languages are compiled languages, then Java and Scala are (lets call it) Semi-Compiled languages and the others are scripting languages. All of these languages have certain advantages over others.

The compiled languages

... have the advantages that they are fast. Haskell and Rust also have the advantages that if the compiler does not fail compiling the sources, the program just works ™.

Also, learning C++, Haskell or Rust would be a really great experience and may be beneficial later in a job or something.

The semi-compiled languages are also (kind of) fast, but they are JVM-languages, so I won't look into them here...

The scripting languages

Writing the application in a scripting language would have some other advantages: They would be much more flexible and I'd be faster writing the application (compared to Rust or Haskell, which I would need to learn with the application). I don't need to learn Ruby, I know a fair bit of Racket and I guess I can get productive with Python or Lua really fast.

Bash is another option and I'm actually an bash script for an almost similar job right now. I could simply extend this script, that would be an option, of course.

So which is the right tool for the job?

Can you tell me? I don't know which tool is the right tool for the job. After all, I don't think I should write such an application from scratch. So, for me, there are two options left: Extending jrnl, which is an Python script, or extending diary (I do not even find a link for this anymore), which is a bash script. Extending the bash script would be an easy job, I guess. But it wouldn't be as flexible as the Python tool is already. tags: #bash #c #c++ #haskell #programming #racket #rust #shell #tools

I love minimalism. I had my prompt long time just beeing a simple status indicator and a pipe, everything which was not that important was ripped out.

At some point, I decided that I need at least the time in my prompt, but I wanted it to be on the right side. I managed to do it. And now, as I have more machines, I need to put the hostname in the prompt. I also added a shell-level indicator in the prompt, a “How many jobs are in the background” indicator was also added some time ago.

This post wants to elaborate how this all was achived while keeping the prompt itself as minimal as possible. At least from my point of view.

Requirements

So first, the requirements:

  • Indicators
    • Did the last command work?
    • How many background jobs do you have?
    • How many shell levels are there?
  • What's the current hostname?
  • What's the time?
  • Where am I?

Also, the bash shouldn't flatter around. I want to have my commands in one column, straight down the line.

The prompt

So, that's what my prompt looks like:

my prompt

As you can see, my hostname is one the left but almost everything else is on the right. Well, there's actually really much on the left!

First, there is the hostname. I managed to get the hostname into a box which is always 8 characters long. So longer host names will fit in there as well and don't break the pipe bar at the end of the left side of the prompt.

Also, the hostname will be green whenever the last command succeeded, turning into red if the last command failed.

After the hostname we have the pipe, which is simply the delimiter between my hostname and my commands. Well, not quite. It also doubles itself in a subshell, tribles itself in a sub-subshell and so on.

On the right side there is another pipe, but this one flutters. After that, we have another two delimiters which could be removed, but I keep them. Then there is the current branch whenever I'm inside a git directory, the current working directory the number of jobs and the time.

Howto

To achive this, you need to write a bit of bash code into your .bashrc, that's all:

#
# ~/.bashrc
#

printfnchr() {
    for i in `seq 1 $1`; do echo -ne "$2"; done
}

shlvl() {
    # I'm always on shell level 4 here, login, xserver, terminal, bash
    # so subtract 3 to get 1 in a normal (non-nested) shell
    echo $((SHLVL - 3))
}

PS1=$'$(if [[ "$?" == "0" ]];then echo "\[\033[32m\]"; else echo "\[\033[31m\]"; fi) $(printf "%8s" "($HOSTNAME)")\e[0m $(printfnchr $(shlvl) "\u2502") '
PS2=$'\[\033[32m\] $(printf "%8s" "($HOSTNAME)") #>\e[0m \u2502 '

rightpromptstring() {
    local tmp=$?
    local dirty=$([[ $(git diff --shortstat 2>/dev/null | tail -n 1) != "" ]] &&
        echo " *")
    local g=$([[ $(git branch 2>/dev/null) ]] &&
        echo " ($(git describe --all | sed 's/^[a-zA-Z]*\///')) ")
    local p=$(pwd | sed "s:^$HOME:~:" | rev | cut -d/ -f 1 | rev)
    local j=$(jobs | wc -l)
    local d=$(date +%H:%M)

    local r=$(echo -n "\u2502 \033[1;30m<<$dirty$g$p [$j] [$d]\033[0m")

    local rl=${#r}
    local t=$(tput cols)
    let "p = $t - $rl + 22"
    printf -v sp "%*s" $p ' '
    echo -ne "$sp$r\r"
    return $tmp
}

PROMPT_COMMAND=rightpromptstring

It is not as complicated as it seems and I'm sure there's room for optimization here and there, but for me it works and it is fast.

Of course, the printfnchr function and also the shlvl function could have been placed right in the PS1 variable, but I want to keep them, maybe I can reuse them later!

The rightpromptstring function is a beast, I know. This would be my first point if I had to optimize this whole thing, but I don't need to, so there is no need to.


I won't go into detail how things work, I assume you know bash. Anyways, feel free to ask me questions about this!


Edit:

To have the pipe character also when logged in via SSH, you have to modify the shlvl function like this:

shlvl() {
    # I'm always on shell level 4 here, login, xserver, terminal, bash
    # so subtract 3 to get 1 in a normal (non-nested) shell
    if [ -z "$SSH_CLIENT" ]
    then
        echo $((SHLVL - 3))
    else
        echo $SHLVL
    fi
}

tags: #linux #bash

I switched to i3 recently and therefor reconfigured my whole system in manner of apperance. I really like it now, as it uses grey as basic color. Everything is grey: My terminal (xterm, bash), my editor (vim), my IRC client (irssi), my music player (ncmpcpp). But what I like most by far, is my prompt.

Bash it to the right

I always hated the flutter of my bash prompt when diving into directories:

~ $ cd foo ~/foo & cd bar ~/foo/bar $ cd ~ $

But now, I finally reconfigured it to be in a straigt line:

:> | cd foo :> | cd bar :> | cd

And the path is now on the right side (I can't show it in the example, unfortunately).

There is not much magic in it... but let me show you:

The magic

The PS1 variable is the one shelter for the prompt. I configured it to be just

:> |

but if the last command failed, it uses the sad smilie:

:< |

(you can do this with Unicode smilies as well!) There is not much magic in it. The pipe symbol is a Unicode character (\u2502) for me, so I get a straight line without gaps between the lines.

The real magic comes now! There is also a bash variable for a command which gets executed for a prompt. And I abuse this variable a little bit. But first, we have to write the command (actually a bash function):

rightpromptstring() { local tmp=$? local dirty=$([[ $(git diff —shortstat 2>/dev/null | tail -n 1) != “” ]] && echo “ “) local g=$([[ $(git branch 2>/dev/null) ]] && echo “ ($(git name-rev HEAD | cut -d ” “ -f 2)) “) local p=$(pwd | sed “s:^$HOME:~:“) local j=$(jobs | wc -l) local d=$(date +%H:%M) local r=$(echo -n “\u2502 \033[1;30m<<$dirty$g$p [$j] [$d]\033[0m”) local rl=${#r} local t=$(tput cols) let “p = $t – $rl” printf -v sp “%s” $p ' ' echo -ne “$sp$r\r” return $tmp }

So, what happens in here? The first line is for saving the return value of the previous command. We need it to be returned lateron, as the smilie from PS1 won't work if we don't return it.

The next few lines do a bit of git magic, to include the current branch and a asterisk if the repository is “dirty”. The variable p gets the path, but if I'm in my home directory, it gets replaced by the tilde. The j variable gets the number of jobs in the bash, as I always want this to be printed somewhere. The d gets the current time.

The r variable now gets the string which represents the right prompt. It also gets the pipe character (actually the Unicode character \u2502) and some color stuff. Then, the rl variable gets the length of the string in r. The t variable gets the number of colums, which is required for printing spaces until we are at the right side (the printf part prints spaces to the variable sp). Then, the spaces and the right prompt get printed. And then simply a \r for carriage-return.

Afterwards we return the stored exit code of the application which just exited and that's it.

Now, just set the prompt command variable:

PROMTP_COMMAND=rightpromptstring

And that's it.

I know, the rightpromptstring function is a bit ugly and I bet it can be improved, but it works very good for me! Feel free to send me patches!

tags: #bash #programming #linux