Tips en Trucs 2018

JPEG bestanden checken

Bij het downloaden, kopiëren, verplaatsen of bewerken worden bestanden soms beschadigd. Zo gebruik ik een script om dagelijks een andere bureaublad achtergrond te downloaden. Heel af en toe gebeurde het dat er een foutmelding i.p.v. een JPEG afbeelding werd gedownload. De hoogste tijd om na het downloaden de JPEG afbeelding te testen.

Een test waarbij je enkel controleert of een JPEG bestand de extensie .jpg heeft is geen zekerheid dat je met een geldige JPEG afbeelding hebt te maken.

De snelle eenvoudig test

De opdracht file achterhaald het formaat van een bestand. Een paar voorbeelden:

dany@pindabook:~> file Documenten/Systeembeheer/wallpaper.sh 
Documenten/Systeembeheer/wallpaper.sh: Bourne-Again shell script, UTF-8 Unicode text executable
dany@pindabook:~> file Afbeeldingen/Achtergronden/02822_daisy_1920x1080.jpg 
Afbeeldingen/Achtergronden/02822_daisy_1920x1080.jpg: JPEG image data, JFIF standard 1.02, aspect ratio, density 100x100, segment length 16, progressive, precision 8, 1920x1080, frames 3
dany@pindabook:~> file Afbeeldingen/Achtergronden/03104_tekapodawn_1920x1080.jpg 
Afbeeldingen/Achtergronden/03104_tekapodawn_1920x1080.jpg: HTML document, ASCII text, with very long lines, with CRLF, CR, LF line terminators

Bij de eerste opdracht wordt het bestand wallpaper.sh geïdentificeerd als een Bourne-Again shell script. Bij de tweede opdracht identificeert file het bestand 02822_daisy_1920x1080.jpg als een JPEG image. Bij de laatste opdracht wordt een schijnbaar JPG-bestand als HTML document geïdentificeerd. Dit JPG bestand werd gedownload, maar door een fout werd i.p.v. een JPG afbeelding een foutmelding in het HTML formaat doorgestuurd.

Map doorzoeken

Door gebruik te maken van jokertekens en filteropdrachten kan je snel volledige mappen doorzoeken. Om alle JPG bestanden (*.jpg) in een map (Afbeeldingen/Achtergronden) te identificeren met de file opdracht:

dany@pindabook:~> file Afbeeldingen/Achtergronden/*.jpg | grep -v "JPEG image data"
Afbeeldingen/Achtergronden/03104_tekapodawn_1920x1080.jpg:                               HTML document, ASCII text, with very long lines, with CRLF, CR, LF line terminators

De file opdracht identificeert alle JPG bestanden en de grep opdracht filtert er alle regels uit die de woorden "JPEG image data" niet (-v) bevatten en dus niet als JPG bestand geïdentificeerd werden.

Om op de gevonden resultaten een bewerking uit te voeren, gebruik je een lus met opdrachten die op elk gevonden resultaat uitgevoerd worden. Om enkel het pad en de bestandsnaam weer te geven, wordt de opdracht:

dany@pindabook:~> file Afbeeldingen/Achtergronden/*.jpg | grep -v "JPEG image data" | while read line ; do echo "${line%%:*}" ; done
Afbeeldingen/Achtergronden/03104_tekapodawn_1920x1080.jpg

Het pad en de bestandnaam worden uit de regel gefilterd door gebruik te maken van tekenreeksbewerkingen (string manipulation) in Bash. De constructie ${line%%:*} verwijdert uit de tekenreeks line de langste tekenreeks te beginnen vanaf het einde (%%) die bestaat uit een dubbel punt gevolgd door om het even wat (:*).

De opdracht wordt een stuk duidelijker als we deze op verschillende regels plaatsen:

dany@pindabook:~> file Afbeeldingen/Achtergronden/*.jpg | grep -v "JPEG image data" | while read line
> do
> echo "${line%%:*}"
> done
Afbeeldingen/Achtergronden/03104_tekapodawn_1920x1080.jpg

Als de opdrachtregel te complex wordt, wordt het tijd om uw opdrachten in een script te plaatsen. Zo kan je dit uitbreiden om de niet als JPEG geïdentificeerde bestanden naar een map te verplaatsen of nog drastischer te verwijderen.

JPEG specialist

De file opdracht identificeert bijna alle bestandsformaten, maar is geen JPEG specialist. De opdracht identify is dat wel. Deze opdracht controleert JPEG bestanden op fouten. M.a.w. als identify zijn fiat geeft, mag je zeker zijn dat het om een geldige JPEG afbeelding gaat. Dezelfde testvoorbeelden, maar nu met identify:

dany@pindabook:~> identify Documenten/Systeembeheer/wallpaper.sh
identify: no decode delegate for this image format `SH' @ error/constitute.c/ReadImage/512.
dany@pindabook:~> identify Afbeeldingen/Achtergronden/02822_daisy_1920x1080.jpg
Afbeeldingen/Achtergronden/02822_daisy_1920x1080.jpg JPEG 1920x1080 1920x1080+0+0 8-bit sRGB 1.04908MiB 0.000u 0:00.009
dany@pindabook:~> identify Afbeeldingen/Achtergronden/03104_tekapodawn_1920x1080.jpg
identify: Not a JPEG file: starts with 0x0a 0x3c `Afbeeldingen/Achtergronden/03104_tekapodawn_1920x1080.jpg' @ error/jpeg.c/JPEGErrorHandler/332.

Enkel de tweede opdracht geeft geen foutmelding en is dus een geldige JPEG afbeelding. Daar identify een onderdeel is van het imageMagick pakket, zullen alle door imageMagick ondersteunde afbeeldingsformaten geen foutmeldingen opleveren. M.a.w. identify controleert allerlei afbeeldingsformaten op fouten.

Als je in een map enkel de ongeldige JPEG bestanden wilt weergeven:

dany@pindabook:~> identify Afbeeldingen/Achtergronden/*.jpg  2>&1 >/dev/null
identify: Not a JPEG file: starts with 0x0a 0x3c `Afbeeldingen/Achtergronden/03104_tekapodawn_1920x1080.jpg' @ error/jpeg.c/JPEGErrorHandler/332.

De identify opdracht (zoals zoveel opdrachten) heeft twee soorten uitvoer (meldingen). Standaard uitvoer om standaard meldingen weer te geven en fout uitvoer om foutmeldingen weer te geven. Met de constructie 2>&1 sturen we de foutmeldingen (2) naar de standaard uitvoer (1), de oorspronkelijke standaarduitvoer wordt naar een zwart gat gestuurd (>/dev/null) waardoor enkel nog de foutmeldingen worden weergegeven. Door gebruik te maken van een while lus kunnen we op de ongeldige JPEG bestanden opdrachten loslaten:

dany@pindabook:~> identify Afbeeldingen/Achtergronden/*.jpg  2>&1 >/dev/null | while read line
> do
> echo "$line" | cut -d'`' -f2 | cut -d"'" -f1
> done
Afbeeldingen/Achtergronden/03104_tekapodawn_1920x1080.jpg

De eerste cut opdracht splits de tekenreeks op het backtick teken (`) en behoudt het tweede stuk (-f2). De tweede cut opdracht splits de tekenreeks op het teken ' en behoudt het eerste stuk (-f1). Bij de eerste cut opdracht bakenen we het splitsteken af met behulp van enkele aanhalingstekens en bij het tweede met dubbele, dit om verwarring met de splitstekens zelf te vermijden.

Elke keer een andere achtergrond

Met het volgende script wordt er per dag indien mogelijk een nieuwe achtergrond via het internet opgehaald. Er worden maximaal 100 achtergronden bewaard. Uit deze 100 achtergronden wordt bij elke systeemstart één willekeurige achtergrond geselecteerd en opgeslagen als ~/Afbeeldingen/Achtergrond.jpg. Als je dit script dus bij elke systeemstart laat uitvoeren en je als Bureaublad-achtergrond de afbeelding ~/Afbeeldingen/Achtergrond.jpg gebruikt, krijg je het gewenste effect.

#!/bin/bash

# Wacht tot de grafische opgeving volledig is opgestart
sleep 1m
# Maak indien nodig een verborgen map voor pinda instellingen aan
if [ ! -d $HOME/.pinda ]; then
  mkdir -p $HOME/.pinda
  touch $HOME/.pinda/wallpaper
fi
# Willekeurige oude achtergrond
achtergrond=`ls /usr/home/Documents/Afbeeldingen/Achtergronden/*.jpg |sort -R |tail -1`
# Haal per dag één nieuwe achtergrond op,
filemtime=`stat -c %Y $HOME/.pinda/wallpaper`
currtime=`date +%s`

if [ $((currtime - filemtime)) -gt 43200 ]; then

  # Set InterfaceLift specifics
  SITE=interfacelift.com
  PAGE=https://$SITE/wallpaper/downloads/random/wide_16:9/1920x1080/index.html
  #echo $PAGE

# Wacht tot het netwerk opgestart is.
  while ! ping -c 1 $SITE; do
    sleep 1m
  done

  # check if InterfaceLift is reachable
  if curl -s --head  --request GET https://$SITE ; then
    # Hou enkel de 100 recenste achtergronden
    numfiles=($HOME/Afbeeldingen/Achtergronden/*)
    numfiles=${#numfiles[@]}
    while [ $((numfiles)) -gt 99 ]; do
      unset -v oldest
      for file in $HOME/Afbeeldingen/Achtergronden/*; do
          [[ -z $oldest || $file -ot $oldest ]] && oldest=$file
      done
      rm $oldest
      numfiles=($HOME/Afbeeldingen/Achtergronden/*)
      numfiles=${#numfiles[@]}
    done
    
    # Eén nieuwe achtergrond per dag ophalen
    touch $HOME/.pinda/wallpaper
    # extract wallpaper of the day url
    WOTD=`wget --user-agent="Mozilla/5.0 (X11; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0" -qO - $PAGE | grep "click here to download" | head -1 | sed -e "s,.*href=\",," -e "s,\",," | cut -d '>' -f 1`
    #echo $WOTD

#     wget --user-agent="Mozilla/5.0 (X11; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0" --directory-prefix=$HOME/Afbeeldingen/Achtergronden/ https://$SITE$WOTD
    wget --user-agent="Mozilla/5.0 (X11; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0" --output-document=$HOME/Afbeeldingen/Achtergronden/$(basename $WOTD) https://$SITE$WOTD
    # Nieuwe achtergrond
    achtergrond=$HOME/Afbeeldingen/Achtergronden/$(basename $WOTD)
    kdialog --title "$titel" --passivepopup "Nieuwe achtergrond opgehaald." 5
  elif curl -s --head  --request GET https://wallpaperscraft.com ; then
    PICURLPAGE=`wget -qO - https://wallpaperscraft.com/all/1920x1080 | awk '/wallpaper_pre/{getline; print}' | head -1 | sed -e "s,.*href=\",," -e "s,\",," | cut -d ' ' -f 1`
    PICURL=`wget -qO - https:$PICURLPAGE | grep "1920x1080.jpg" | head -1 | sed -e "s,.*src=\",," -e "s,\",," | cut -d ' ' -f 1`
    wget -O $HOME/Afbeeldingen/Achtergronden/$(basename $PICURL) https:$PICURL
    achtergrond=$HOME/Afbeeldingen/Achtergronden/$(basename $PICURL)
    kdialog --title "$titel" --passivepopup "Nieuwe achtergrond opgehaald." 5
  fi

fi
titel="Ophalen achtergrondafbeelding"
if file "$achtergrond" | grep 'JPEG image data' ; then
  cp "$achtergrond" $HOME/Afbeeldingen/Achtergrond.jpg
else
  rm "$achtergrond"
  kdialog --title "$titel" --passivepopup "De opgehaalde achtergrond was geen JPEG afbeelding." 5
fi

Sla dit script op (bijvoorbeeld als ~/Documenten/Systeembeheer/wallpaper.sh) en maak het met de volgende opdracht uitvoerbaar:

dany@pindabook:~> chmod +x Documenten/Systeembeheer/wallpaper.sh

Test het script grondig in de terminal voor je het bij elke systeemstart laat uitvoeren. Door de sleep opdracht, wordt het script pas echt gestart na 1 minuut. Deze wachttijd zorgt ervoor dat het systeem volledig is opgestart voor het script zijn werk doet.

dany@pindabook:~> Documenten/Systeembeheer/wallpaper.sh 
/usr/home/Documents/Afbeeldingen/Achtergronden/03749_deepblue_1920x1080.jpg: JPEG image data, Exif standard: [TIFF image data, big-endian, direntries=16, height=2750, bps=0, compression=LZW, PhotometricIntepretation=RGB, manufacturer=SAMSUNG, model=NX10, orientation=upper-left, width=4400], progressive, precision 8, 1920x1080, frames 3

Daarna kan je via de Systeeminstellingen van KDE in de rubriek Opstarten en afsluiten in het onderdeel Autostart het script toevoegen om het bij elke systeemstart te laten uitvoeren.

Autostart