Schlagwort-Archive: Bash

Zwei Bash-Skripte zur Analyse der NGINX Access Logs

Beim Durchwühlen des Internets bin ich auf eine Perle gestoßen, die ich hier festhalten möchte. In „Bash Script to Parse and Analyze Nginx Access Logs“ stellt Ruan Bekker ein kurzes Bash-Skript vor, welches die NGINX Access Logs analysiert, um einen Bericht mit folgenden Sektionen auszugeben:

  • Top 10 Request IPs (aus dem aktuellen Access Log)
  • Top Request Methods (aus dem aktuellen Access Log)
  • Top 10 Request Pages (aus dem aktuellen Access Log)
  • Top 10 Request Pages (aus dem aktuellen und Gzipten Logs)
  • Top 10 HTTP 404 Page Responses (aus dem aktuellen und Gzipten Logs)

Ich selbst nutze aktuell den Fork von Marc Brunet, welchen ich meinen My-IT-Scripts hinzugefügt habe:

#!/bin/bash

# URL: https://github.com/Tronde/My-IT-Scripts/blob/master/bash/analyze_nginx_access_logs.sh

# variables
LOGFILE="/var/www/jkastning/sites/logs/www.my-it-brain.de_access.log"
LOGFILE_GZ="/var/www/jkastning/sites/logs/www.my-it-brain.de_access.log.*"
RESPONSE_CODE="200"

# functions
filters(){
grep -w $RESPONSE_CODE \
| grep -v "\/rss\/" \
| grep -v robots.txt \
| grep -v "\.css" \
| grep -v "\.jss*" \
| grep -v "\.png" \
| grep -v "\.ico"
}

filters_404(){
grep -w "404"
}

request_ips(){
awk '{print $1}'
}

request_method(){
awk '{print $6}' \
| cut -d'"' -f2
}

request_pages(){
awk '{print $7}'
}

wordcount(){
sort \
| uniq -c
}

sort_desc(){
sort -rn
}

return_kv(){
awk '{print $1, $2}'
}

request_pages(){
awk '{print $7}'
}

return_top_ten(){
head -10
}

## actions
get_request_ips(){
echo ""
echo "Top 10 Request IP's:"
echo "===================="

cat $LOGFILE \
| filters \
| request_ips \
| wordcount \
| sort_desc \
| return_kv \
| return_top_ten
echo ""
}

get_request_methods(){
echo "Top Request Methods:"
echo "===================="
cat $LOGFILE \
| filters \
| request_method \
| wordcount \
| return_kv
echo ""
}

get_request_pages_404(){
echo "Top 10: 404 Page Responses:"
echo "==========================="
zgrep '-' $LOGFILE $LOGFILE_GZ\
| filters_404 \
| request_pages \
| wordcount \
| sort_desc \
| return_kv \
| return_top_ten
echo ""
}


get_request_pages(){
echo "Top 10 Request Pages:"
echo "====================="
cat $LOGFILE \
| filters \
| request_pages \
| wordcount \
| sort_desc \
| return_kv \
| return_top_ten
echo ""
}

get_request_pages_all(){
echo "Top 10 Request Pages from All Logs:"
echo "==================================="
zgrep '-' --no-filename $LOGFILE $LOGFILE_GZ \
| filters \
| request_pages \
| wordcount \
| sort_desc \
| return_kv \
| return_top_ten
echo ""
}

# executing
get_request_ips
get_request_methods
get_request_pages
get_request_pages_all
get_request_pages_404

Selbstverständlich erhalte ich damit keine genauen Statistiken, da meine Logs nach einem Monat automatisch gelöscht werden. Für einen kurzen Rückblick und der Erstellung eines monatlichen Berichts scheint das kleine Skript jedoch gut geeignet zu sein. Ich probiere es gerade aus, um zu sehen, wie gut es mir auf Dauer gefällt.

Auf Basis des ersten Skripts habe ich ein zweites geschrieben, mit dessen Hilfe ich die Requests für einen spezifischen Beitrag abfragen kann (Quelle):

#!/bin/bash

# variables
LOGFILE="/var/www/jkastning/sites/logs/www.my-it-brain.de_access.log"
LOGFILE_GZ="/var/www/jkastning/sites/logs/www.my-it-brain.de_access.log.*"
RESPONSE_CODE="200"
ARG1=$1

# functions
filters(){
grep -w $RESPONSE_CODE \
| grep -v "\/rss\/" \
| grep -v robots.txt \
| grep -v "\.css" \
| grep -v "\.jss*" \
| grep -v "\.png" \
| grep -v "\.ico"
}

request_ips(){
awk '{print $1}'
}

request_page(){
awk '{print $7}' \
| grep -w $ARG1
}

wordcount(){
sort \
| uniq -c
}

return_kv(){
awk '{print $1, $2}'
}

get_request_page(){
echo "Page requests in current log:"
echo "====================="
cat $LOGFILE \
| filters \
| request_page \
| wordcount \
| return_kv
echo ""
}

get_request_page_all(){
echo "Page requests in all logs (last month):"
echo "==================================="
zgrep '-' --no-filename $LOGFILE $LOGFILE_GZ \
| filters \
| request_page \
| wordcount \
| return_kv
echo ""
}

# execute
get_request_page
get_request_page_all

Der folgende Code-Block zeigt ein Beispiel, wie das Skript angewendet wird. Dabei wird der Permalink als Argument übergeben:

:~/bin$ sh get_page_requests_from_nginx_access_logs.sh kommentar-linux-container-spreu-und-weizen
Page requests in current log:
=====================
262 /wordpress/kommentar-linux-container-spreu-und-weizen/
6 /wordpress/kommentar-linux-container-spreu-und-weizen/feed/

Page requests in all logs (last month):
===================================
5124 /wordpress/kommentar-linux-container-spreu-und-weizen/
49 /wordpress/kommentar-linux-container-spreu-und-weizen/feed/
2 /wordpress/wp-json/oembed/1.0/embed?url=https://www.my-it-brain.de/wordpress/kommentar-linux-container-spreu-und-weizen/

Noch nicht schön, aber zweckmäßig.

Was haltet ihr davon? Falls ihr beim Drübergucken zufällig noch einen Fehler in den Skripten entdeckt, freue ich mich, wenn ihr mir einen Kommentar hinterlasst.

Bildbearbeitung in der Kommandozeile

Auf Ausflügen und im Urlaub begleitet mich neben meiner Freundin meist auch meine kleine Canon IXUS, um die schönsten Urlaubsbilder festhalten zu können. Die Fotos mache ich meist mit der höchsten Auflösung. Falls ich später ein Bild in Postergröße drucken lassen möchte habe ich so keine Probleme wegen einer zu geringen Auflösung. Jedoch belegen Bilder in Hoher Auflösung schnell 3-4MB pro Bild was die SSD bei vielen Bildern sehr schnell füllt.

Zu den regelmäßigen Aufgaben nach dem Urlaub gehört daher das Bilder durchsehen und verkleinern. Denn zum Zeigen auf den Notebook oder Tablet reicht meist auch eine wesentlich kleinere Auflösung und man spart jede Menge Speicherplatz ein.

Das Verkleinern der Bilder erledige ich schnell und einfach auf der Kommandozeile. Dazu habe ich mir folgendes Skript erstellt, dem ich beim Aufruf die gewünschte Auflösung mitgebe und schon wandelt es alle Bilder im angegebenen Verzeichnis um. Dabei werden keine neuen Dateien angelegt, sondern die vorhandenen bearbeitet. Daher sollte man sicher sein welche Auflösung man will.

#!/bin/bash
# Bildgroesse reduzieren und Seitenverhaeltnis beibehalten
# Die gewuenschte Aufloesung wird abgefragt
# Author: Joerg Kastning
# Lizenz: GPLv3
# Programmbeginn
read -p "Bitte die gewünschte Auflösung eingeben (z.B. 1600): " aufloesung
ls -1 *.JPG *.jpg | while read file;
do {
mogrify -resize "$aufloesung"x"$aufloesung" "$file"
echo "Bild $file wird verkleinert."
}
done
echo "Die Bearbeitung ist beendet."
exit

Möchte man seine Bilder nun noch ins Web hochladen, um sie mit Freunden zu teilen kann man den Bildern ein Wasserzeichen hinzufügen. Wasserzeichen schützen eure Bilder davor, dass andere sie als ihren eigenen Schnappschuss ausgeben. Denn sind eure Bilder erstmal im Netz kann sie dort so gut wie jeder finden und kopieren. Beim hinzufügen der Wasserzeichen hilft das folgende Skript welches ich im Wiki von Ubuntuusers gefunden habe.

#!/bin/bash
# Wasserzeichentext in alle JPEG Bilder aus diesen Verzeichnis einfuegen
# Der Wasserzeichentext wird unten links ins Bild eingebracht
# Sie koennen folgende Parameter anpassen:
Textabstandvonlinks=10
Textabstandvonunten=10
Schriftgroesse=10
PfadFonts="/usr/share/fonts/truetype/msttcorefonts"
# Pfad ist je nach Distribution unterschiedlich!
Schriftart="Arial.ttf"
Schriftfarbe="white"
# Moegliche Farben koennen aufgelistet werden mit dem Befehl: convert -list color
Wasserzeichentext="Copyright Joerg Kastning"
# Programmbeginn
echo "Textabstand von links: $Textabstandvonlinks"
echo "Textabstand von unten: $Textabstandvonunten"
echo "Schriftgoesse: $Schriftgroesse"
echo "Schriftart: $Schriftart"
echo "Schriftfarbe: $Schriftfarbe"
echo "Wasserzeichentext: $Wasserzeichentext"
echo " "
ls -1 *.JPG *.jpg | while read file;
do {
horizontal=`identify -verbose $file | grep Geometry: | awk {'print $2'} |cut -d"x" -f 1`
vertikal=`identify -verbose $file | grep Geometry: | awk {'print $2'} |cut -d"x" -f 2`
X=$Textabstandvonlinks
Y=$(($vertikal - $Textabstandvonunten))
convert
-font $PfadFonts/$Schriftart -pointsize $Schriftgroesse -fill
$Schriftfarbe -draw "text $X, $Y '$Wasserzeichentext'" "$file"
"`basename Wasserzeichen_"$file"`";
echo "Bearbeite Datei $file"
}
done
echo "Wasserzeichen wurden erfolgreich eingearbeitet"
exit
# Programmende

Diese beiden Skripts machen aus der Skalierung und dem Hinzufügen von Wasserzeichen ein Kinderspiel. So macht Bildbearbeitung in der Kommandozeile Spaß.

Ergänzung vom 15.12.2017

Beim Aufräumen und Sortieren meiner Bilder entstand eine neue Anforderung, die ich mit den beiden bisherigen Skripten nicht abdecken konnte. Datum und Uhrzeit der Aufnahme, sollten aus den EXIF-Informationen extrahiert und in den Dateinamen kodiert werden. Diese Aufgabe werde ich fortan mit folgendem Python-Skript erledigen:

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
#
# Beschreibung:
# Dieses Skript dient der Verarbeitung von JPEG-Dateien. Es wertet das
# Attribut 'EXIF DateTimeOriginal' aus und benennt die verbeiteten Dateien
# um, so dass diese dem Muster '_img.jpg' entsprechen.
#
# Autor:    Tronde (https://ubuntuusers.de/user/Tronde/)
# Lizenz:   GPLv3 (http://www.gnu.de/documents/gpl.de.html)

import os
import exifread
import argparse

def main():
    parser = argparse.ArgumentParser(description="Dieses Skript dient der Verarbeitung von JPEG-Dateien. Es wertet das Attribut 'EXIF DateTimeOriginal' aus und benennt die verbeiteten Dateien um, so dass diese dem Muster  entsprechen.")
    parser.add_argument("-s", "--source-dir", required=False, default='.', dest="src", help="Quell-Verzeichnis mit den zu verarbeitenden JPEG-Dateien.")

    args = parser.parse_args()
    
    src = args.src

    os.chdir(src)
    for file in os.listdir(src):
        if file.endswith(".jpg") or file.endswith(".JPG"):
            f = open(file, 'rb')
            tags = exifread.process_file(f, details=False, stop_tag='EXIF DateTimeOriginal')
            str_DateTime = str(tags['EXIF DateTimeOriginal']).split()
            str_Date = str(str_DateTime[0]).split(':')
            str_Time = str(str_DateTime[1]).split(':')
            Date = str_Date[0] + "-" + str_Date[1] + "-" + str_Date[2]
            Time = str_Time[0] + str_Time[1] + str_Time[2]
            fname = Date + "T" + Time + "_img.jpg"
#            print(fname)
            os.rename(file,fname)

if __name__ == "__main__":
main()

Das obige Skript

  1. liest alle JPG-Dateien eines Verzeichnisses ein,
  2. wertet die EXIF-Informationen aus und
  3. benennt die verbeiteten Dateien um, so dass diese dem Muster ‚_img.jpg‘ entsprechen.

Alles Skripte finden sich auch auf GitHub im Repository: https://github.com/Tronde/Bildbearbeitung