Entries tagged 'cat:Bash'

SBWG 0.10.7

So many ideas, so little time. So many todos, no little time. So little time, so much rest of my life. But the todo list has actually started to become a little shorter for the first time in a long time. As with the previous entries about my progress with SBWG, I'm not going to address or list everything that I've improved or added. But I feel like it's time for another update.

File attachments pretty much work as intended now. Text files still can't be file attachments to entries, because text files are entries. But I think that will stay as it is for now because I don't need to attach text files to entries and if I would, I could gzip, bzip or 7z them. I mainly wanted file attachments for entry images and podcast support. Both works as well as I need it to. But some things could still be improved in the future.

Referencing tags are improved, their notes look better and there is now a way to mark an entry as an update of an other entry, giving both entries a note that tells the reader about the newer/older version. And since this works now, I dropped the idea to introduce any form of content revisioning. An entry revision system would be over the top for a feature that I hardly use. This could still be implemented just with hooks.

SBWG was relying on a feature of ImageMagick's mogrify that turned out to be a bug and was fixed. So I had to switch to convert with worse tolerance/support for broken image files. That's annoying, but I'm over it now. I mean, I wasn't complaining. Those tools are amazing and I'm so glad that they are free.

There were a number of other bugs that I've fixed. Some of them old and overlooked. But most of them were introduced not long ago. Well, I guess that's what happens if you don't do proper tests every time. SBWG is starting to become a feature-rich tool and every new feature that gets implemented could cause trouble with other features or combinations of features. In early versions of SBWG I didn't always test everything I changed. I now do, and I now also try to think of some related things the change could have effected unintentionally and test those basically. But proper tests of all sorts of scenarios every time I publish a new version? That would be professional but not worth the effort in this case. If I'm not using a feature, it doesn't matter much if I break it. I think I'll probably do more extensive tests every now and then.

Entries can now have comment links. If there is an email address available for an author, all their entries get an email link below them to allow readers to reply to them. For a public blog it might be best to use throwaway addresses that can quickly be replaced if they start to receive spam. I'd have some ideas to improve the email composed by this comment feature. But they would have to become HTML emails. So I forgot about them again.

New tag types, expanded README file, improved navigation by adding parent links and things like this, improved CSS a bit here and there, very small improvements to the code style. I didn't make any relevant improvements or corrections in terms of code quality. I think this is where SBWG could improve most. It's not a professional thing, so I declare myself fine with the way it is (which isn't that bad compared to some other software, I think) but I could learn a lot there.

Command line options can now be grouped to make typing commands easier. Long options (starting with --) didn't change (I may make those things POSIX-like, too, some other time) but short options (starting with a -) now don't need to be separated and all have their own dash. So sbwg -v -p -e blub can now be shortened to sbwg -vpe blub. And if you want to run the command again but with added verbosity, you can add the second -v wherever you want.

There is now a waiting animation that starts to play if there is no update on stdout for three seconds. Both the animation frames and the time delay can be changed. The animation is only enabled if SBWG runs with any verbosity. Then it is enabled by default. It will be disabled by default in future versions though, I think, because it's such an unnecessary messing around with terminal output. It does work pretty well though. I thought it may be harder to make it work as smoothly as it does now (which isn't 100% perfect, but pretty good).

The new default style set 'elth' is a working hybrid with different styles for small phone screens and larger screens of phablets, tablets and laptops. It's composed of several CSS files, which should make it easier to create a custom style now, since just the basics (without any colors and fancy embolishment and without any tag-specific stuff) can be copied for a template, or just the mobile style can be copied and used to create a new mobile style. Style sets can now also include files whose name don't end in '.css'. That means they can contain any file type. So JS or background images or whatever will be copied as part of the style set and placed in the 'styles/' directory of the generated web site.

More things are now cached that may be used more than once in one run of SBWG. Permament caches (option -C/--cache) can now be enabled for only parts of the web site. I still need to decide how permanent caches should be managed. The way I had/have it in mind does not seem very intuitive. It is to me because I know how it's built. But... well, maybe it's enough that I think that it makes sense. Maybe I should just start using it and if it works well, write it down in the README file.

Added new hooks, gave the helper scripts makeovers, made how HTML footers are made logical, sorted much of the remaining todos. Overall: progress.

So, I've published SBWG version 0.10.7 now. I wish I'll find the time to create and publish new versions more frequently again in the near future. But I doubt I will.


SBWG 0.10.2

I know, my entries on SBWG are too chaotic to follow what's happening with the project in detail. But who cares, really? I'll just continue to write broadly about the progress that I make with the script.

So, after re-deciding that I'm not going to make a version 1.0 because it's just not the kind of software that can ever claim for itself the thoroughness that I myself would expect from a piece of software that's labeled v1.0, I finished the testing and bug squashing that I, for a short while, had intended to lead to version 1.0 to my satisfaction, and started implementing new features again. Great, now I can make the script take longer to finish again. :)

During my testing and bug squashing spree I already implemented a routine that I call the pathreducer. If enabled through command line option, it reduces paths to conform with the file and directory name restrictions of most file systems out there. I took this a bit far and it can now even conform to filenames of the 6.3 format, if the user wishes. I have ideas to improve this further, including making it automatically conform to the exact limitations of individual file systems instead of stripping and replacing characters that aren't allowed in file names in many file systems whether it would have been necessary in the present case or not. But that's for another time. For now it works well for what it is supposed to be: a workaround for too long and otherwise invalid file names that would be generated by the script when naming files after categories, topics and other tags, as well as a solution to problems that might occur if SBWG would be used to generate a web site onto a file system that has more restrictions than those commonly used on Unix-like systems.

As part of the improvements made during the no-new-feature time, I fixed many bugs that I didn't expect at all when I started (which is why I prohibited myself from implementing new features in the first place), learned a lot about Bash again, and made some small improvements that I wouldn't call new features, but are still neat to have. One of them makes tag lines more intuitive. Forbidden characters are removed or replaced and leading and trailing whitespaces are removed, leading coincidentally to the fact that you can use a space after the tag type (e.g. 'cat: Example' instead of 'cat:Example') or not and the tag will be recognised as being identical in both cases. I'm mentioning this because the first person to type a SBWG tag line besides me typed a space intuitively and I had to tell them that that's not how that works. Well, now it is, if you prefer it. Maybe case-insensitivity will be next. I'm considering it. Wouldn't be hard to add. I'm not sure if I want it, though.

Another thing that improved drastically in that time is the time it takes to generate huge web sites. SBWG never had problems with giant files, even entry files with gigabytes of text, because the content body isn't parsed. But with thousands of entries or thousands of tags in one entry, generation time would previously go into hundreds of years (theoretically/calculated, of course). By improving some routines and caching some parts during the generation process I got the generation time of an example torture web site that I created, from over 300 years down to just short of 72 hours on my Core-i5 ultrabook onto an NVMe drive. Any reasonably sized web site even with a relatively large weblog is completely generated much quicker. So it would be possible to e.g. let SBWG re-generate a web site automatically multiple times a day without problems. I have other ideas that will speed up the generation process significantly, namely parallel processing and skipping of existing unchanged content. But both those features are not mature enough, yet. I will probably attend to them again when I have packed SBWG with so many features that slow down the process compared to the current version that I feel more parallelisation and more drastic caching will be necessary.

So, the first new feature on the list were entry pictures. A way to bind pictures, image files, to entries without having to include <img> tags. This feature now already developed into a general way to attach files to entries. Images are resized, embedded and linked to their originals, audio files are embedded as an audio player if the browser supports this, and other files are linked to allow visitors to download them. Video files will also be embedded as a player in future version. The file attachments feature is not done, yet. Enclosures in the RSS feed, presentation of the download links and many small things have to be improved. But the initial idea of entry pictures already works. There is a naming convention that makes a file an attachment to a certain entry. (The same naming convention as is used for style sheet sets.) See the README of version 0.10.2 or above for detailed information.

The next feature on the list is comment links below blog entries. Simple mailto links to let readers e-mail comments on entries to the author. I will probably start to work on multiple features at the same time again, as I did before.

So, I've published SBWG version 0.10.2 now. I hope I'll find the time to create and publish new versions more frequently again in the near future. But I doubt I will.


SBWG 1.0.0 Delayed To Next Life

I've decided to not publish version 1.0.0 of SBWG.

Very early on I devided against a versioning approach that allowed me to stay below version 1.0 for a very long time by only incrementing the version number by 0.0.1 for important changes but rather chose to increment the version and publish a new version whenever. I stick to this approach because it allowes me to express felt overall progress in the version number. But now that the goals that I at some point set for version 1.0.0 are nearly reached (they pretty much are), this leaves me in a spot where publishing version 1.0.0 would be the next thing to do despite the fact that it's not actually really absolutely perfect, yet. Absolute perfection isn't really my approach. It's still more like a learning project.

So to avoid having published a version that looks perfect on the label but isn't inside, I will skip this version number. Don't get me wrong. My goals are. I've tested it more than I thought I would and thought of potential problems and fixed bugs that I'd argue are not something you'd expect most shell scripters to catch. It definitely works reliably for what it is intended and at least works well for much more than I thought it would in the first few months of starting this project.

So, there is no link in this entry. No new version published today. The next version will be 1.x.x something.

Alright. Now that that's done, I can start to implement new features again next year.


SBWG - The Pathshortener And Other Recent Changes

I made it my goal to harden SBWG before I start to implement new features. Before I call the next version of this project 1.0 I want to make sure that unexpected input from the command line or from source files, absurd numbers of absurdly long tags and content items, stupidly weird filenames or random binary data as tag values as well as purposfully created traps in the various places where input is processed are handled well, meaning that nothing fails unless there is no sensible way around it, and if something fails, that nothing breaks. Data should be filtered carefully, errors should be handled well and whereever possible data should be made processible if it was supplied in an unprocessible form to reduce the chances of errors. On top of that I wanted to make sure that the script did its job in a reasonable amount of time considering the circumstances. I mean, it will never be very fast. Bash is just not the right language for that. But there certainly were some repetitive tasks that could be improved. Fot the latter I created a simple caching functionality that will probably be extended in the future. I managed to reduce the (calculated/estimated) generation time of my biggest test web site from almost 300 years to a few days. Actual web sites will of course not take that long to generate, even on a slow machine. A huge web site will maybe take up to one day to generate completely, even without the new options that keep the script from re-generating existing unchanged parts of the web site. But before sombody will try to create such a big web site with SBWG I will probably have improved speed further. And even then it's a worst-case time.

As part of the aforementioned goals I have started working on last new feature before version 1.0. I call it the pathreducer. Since many of the files created by SBWG are named after the tags they represent, they can become quite long and contain almost any printible character, including multi-byte unicode characters or characters of character sets I haven't even heard of. I definitely don't want to restrict more than I already have what characters and how many of them tag values can contain. Especially filesystems used by operating systems from Microsoft are relatively restrictive in maximum allowed directory, path and filename length and allowed characters. By default the pathreducer is not used. But if enabled via command line option or in a web site's settings file, it will filter directory and filenames and shorten them to a user-defined maximum length. If the pathreducer decides to change a path elements it also adds a 6-character hash value to make shortened or otherwise reduced path elements as good as unique.

That works well for now and even can create 8.3 or 6.3 filenames for old DOS filesystems. But the result is not very nice because it isn't aware of what filesystem it is going to write a file to. To be save it removes more characters than it would have to for ext and NTFS filesystems. In the future I may extend the pathreducer to detect the filesystem at least of the root of the output directory automatically and decide how exactly path elements should be reduced according to the actual limitations of the present filesystem. Than it may even be enabled by default, even though it can increase the generation time quite a bit.

There are still some tests that I want to do and I will probably find some more things that I want to fix before version 1.0. But I see light.



This entry is an update of the entry 'Backup Shell Script'.

I've updated/improved my backup script again.




function usage () {
  echo "This script requires either one or two arguments."
  echo "Usage:"
  echo "$0 JOBLIST"
  echo "JOBLIST File that contains one backup job per line in the format SOURCE_DIRECTORY NAME"
  echo "SOURCE_DIRECTORY Directory that should be backed up. Please, no trailing slash."
  echo "NAME A string that is used to name the backup in the destination."
  exit 1

function single() {
  if grep -qs "$budir " /proc/mounts
    printf "\n"
    printf "Attempting backup of source directory '%s' to '%s'.\n" "$srcdir" "$fullname"
    printf "Number of differential backups to keep: %s\n" "$num"
    printf "Removing oldest backup... "
#    rm -rf "$fullname.$num"
    rsync --archive --delete "$budir/empty/" "$fullname.$num/"		# This is quicker than rm.
    printf "Done.\n"
    for ((i=$num; i>=2; i--)); do
      printf "Renaming '%s' to '%s' ... " "$fullname.$((i-1))" "$fullname.$i"
      mv "$fullname.$((i-1))" "$fullname.$i"
      printf "Done.\n"
    printf "Duplicating last backup ('%s' to '%s')... " "$fullname.0" "$fullname.1"
    [[ -d $fullname.1 ]] && exit 1							# This directory should not exist at that point.
    cp -al "$fullname.0" "$fullname.1"
#    rsync --archive --acls --xattrs --hard-links "$fullname.0/" "$fullname.1/"
    printf "Done.\n"
    printf "\n\n\n" >> "$fullname.log"
    printf "STARTING INCREMENTAL BACKUP AT %s\n" "$now" >> "$fullname.log"
    printf "Starting new incremental backy uppy at '%s.0'..." "$fullname"
    if rsync --exclude ".cache" --archive --no-links --delete "$srcdir/" "$fullname.0/" 2>&1 | tee -a "$fullname.log"
      printf " Done.\n"
      printf "Done.\n" >> "$fullname.log"
      printf " Failed.\n"
      printf "Failed.\n" >> "$fullname.log"
    printf "\nIf this line is here the script finished (with or without errors) at %s\n" "$now" >> "$fullname.log"
    printf "'%s' is not mounted. Aborting." "$budir"
    exit 1

function fromlist() {
  while read job; do
    if [ -n "$job" ]; then
      name="${job#* }"
      srcdir="${job% *}"
  done < "$list"

mkdir "$budir/empty"									# Empty directory for a quicker method to delete a large directory.

case $# in
  mount "$budir"
  printf "Reading backup jobs from list. Defaulting to %s.\n" "$list"
  mount "$budir"
  printf "Reading backup jobs from %s." "$list"
  mount "$budir"
#  I can't decide whether to make the third argument num or budir. I don't need it anyway.
#  num=$3
#  budir=$3
#  mount $budir
#  single

exit 0

Generating Bitmap Files With Bash

I needed a large amount of image files to try something. I wanted them to be different images. But what's in them didn't matter. So I looked at a BMP file to see how I could create one byte for byte automatically. Bitmap is just the first uncompressed format that I thought of. This is what I came up with:

rbmp() {
  echo -n -e '\x42\x4D\x2A\x02\x00\x00\x00\x00\x00\x00\x7A\x00\x00\x00\x6C\x00\x00\x00\x10\x00\x00\x00\x09\x00\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00\xB0\x01\x00\x00\x23\x2E\x00\x00\x23\x2E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\x47\x52\x73\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x'
  c=$(</dev/urandom tr -dc '0123456789ABCDEF' | head -c864 | sed 's/.\{2\}/&\\x/g')
  echo -n -e "$c"'00'

Or, if you would like to do run a command for each pixel/color before it is generated, you can do it like this:

# Create a 24 bit bitmap file of 16x9 randomly colored pixels
randombmp() {
  echo -n -e '\x42\x4D\x2A\x02\x00\x00\x00\x00\x00\x00\x7A\x00\x00\x00\x6C\x00\x00\x00\x10\x00\x00\x00\x09\x00\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00\xB0\x01\x00\x00\x23\x2E\x00\x00\x23\x2E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\x47\x52\x73\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  while (( i < 433 )); do
    c=$(</dev/urandom tr -dc '0123456789ABCDEF' | head -c2)
    echo -n -e '\x'"$c"

I'm sure I did something wrong. I just copied the header of some BMP file with the right format that I don't even know what it was created with. I didn't actually look up how the header of a BMP file is composed. But it worked.


SBWG 0.9.6

As foreseen I've made slow progress in development of SBWG, the script that generates this web site, because I want to test and improve it with the current feature set before I start to implement new features. The former is necessary. The latter is more fun. But today I've reached a point where I can say that the only thing left to do before I call it v1.0.0 is testing edge cases and things that I didn't think of testing before, as well as fixing potential bugs discovered from this testing.

So, version 0.9.6 is out, everything is working, The README file, other documentation included in the package, the example website, the included style sets and partly even the code quality has been deemed satisfactory, and I hope I'll find the time to test all sorts of weird stuff and discover and fix some bugs next month, at which point version 1.0.0 will be published and I'll finally be able to allow myself to start working on new features, some of which are awaited by both regular users of the script.


Backmatch - A Simple Dual-N-Back-Inspired Performance Task Trainer For Bash

Here is a script that I wrote as a short side project because I wanted my own n-back trainer. I'm aware that the practical memory improments of n-back training, even when using a proper audiovisual dual n-back trainer, is not as great as it's often said to be. I just wanted to try it and see whether I like the training.


Execute the script and pass it a number that resembles the difficulty level. For example bash backmatch 3 starts the script with a 3-back task. On your keyboard press the key of the letter that was displayed n letters ago (in this example 3 letters ago). When you press a key the next letter is presented immedietly. If you don't press a key for 3 seconds (the time value can be changed by changing the variable sec) the next letter is presented and you don't get a point for this letter. When you exit the script by pressing ctrl+c your keypresses get compared to what was presented and you get your score.


if [[ ${#} -ge 1 ]] && [[ ${1} =~ ^[0-9]*$ ]]
  echo "'${@}' is not a number, is it?"
  exit 1

echo "Ctrl+C pressing is for quitters."

end() {
  echo -en "           \n"
  if [[ ${#str} -ge ${npo} ]]							# If enough characters had been generated
    for i in $(seq $n); do echo -n "-"; done					# offset by $n dashes
    echo ${str}
    echo ${you}
    for i in $(seq ${#str})
      [[ ${str:$i:1} == ${you:$((i+n)):1} ]] && got=$((got+1))
    echo -e "\n${got} out of $((${#str}-npo)) correct"
    echo "Not enough data to judge you."

trap end EXIT

while true
  str+=$(cat /dev/urandom | tr -dc 'A-Z' | head -c 1)				# Get a random new letter.
  echo -en "\r       \r${str: -1}"						# Print the last character in the string (the new letter).
    read -n 1 -t ${sec} key							# Get a single character input, timeout $sec seconds.
    [[ -n ${key} ]] && you+=${key^^} || you+="-"
    [[ ${key^^} == ${str: -$npo:1} ]] \
      && echo -en "\r       good" \
      || echo -en "\r        bad"						# Check if uppercase input char is the same as the nth char from the back.


exit 1