Benutzer-Werkzeuge

Webseiten-Werkzeuge


scripts:search_usb_storage_device

USB-Laufwerk per Script finden

Es gibt zwar nur wenige Verwendungszwecke für das nachfolgende Script, aber ab und an braucht man auch mal eine Möglichkeit einer Auswahl von USB-Laufwerken vorzugeben und auswählen zu lassen. In meinem Fall brauche ich eine solche Funktion für ein Programm, das es als Linux- und Windows-Version gibt und auf die gleiche Datenbank zurückgreifen kann. Daneben kann man dieses Script auch dazu verwenden, um ein Backup auf ein frei wählbares USB-Laufwerk zu machen (hier nutze ich derzeit noch vordefinierte Devices, aber das kann man jetzt auch flexibel gestalten).

USB-Laufwerke finden

Linux protokolliert (fast) alle Aktivitäten des Kernels in /var/log/messages. Daneben gibt es einen Ring-Buffer für Kernelmeldungen. Ersteres ist für diese Zwecke eher ungeeignet, daher verwendet das Script letzeres über dmesg.

Liste erstellen

SCSI_DEVBUS=`dmesg | awk -Fscsi '/scsi.*usb-storage/{ print $2 }' | awk '{ print $1 }'`
 
if [ -z $SCSI_DEVBUS ]; then
        SCSI_DEVBUS=`dmesg | awk '/usb-storage: device found at/{ print $NF-1 }'`
fi

Ich habe auf zwei verschiedenen Systemen unterschiedliche Meldungen des usb-storage-Modules erhalten. Die erste Zeile sucht auf neueren Kerneln (ich habe hier mit Kernel 2.6.36.2 getestet), falls da kein Ergebnis kommt, wird die zweite Variante (getestet auf 2.6.31.5) aufgerufen. Beide Zeilen scannen die Ausgabe des usb_storage-Modules und geben bei Erfolg ein Array zurück. Da es aber nur die Nummer des SCSI-Buses zurück gibt, muss man diese auf den Devicenamen übersetzen. Dazu wird mittels einer Schleife jedes Element nochmals in der Ausgabe von dmesg gesucht und die Angabe des Devices ausgefiltert.

SCSI_DEVNAME=`dmesg | grep -m 1 "sd $SCSI_DEVBUSNR.*\[" | awk -F[ '{print $NF}' | awk -F] '{print $1}'`

Das zurückgegebene Device muss aber noch auf Gültigkeit geprüft werden. Doppelte Einträge und nicht mehr eingebundene Geräte müssen entfernt werden. Erst dann darf ein Device auch in das Array aufgenommen werden.

Liste prüfen und bereinigen

Mittels SCSI_DEVNAME wird das Verzeichnis in der Ausgabe von mount gesucht. Da in der Ausgabe von dmesg mehrfache Einträge des gleichen USB-Laufwerks vorkommen können, gibt es auch die Gefahr der Mehrfachauflistung. Die Funktion in_array() arbeit ähnlich wie die bei PHP und durchsucht ein Array auf ein bereits vorhandenes Datum. Ist das Device bereits in der Liste vorhanden, wird es nicht mehr aufgenommen. Parallel wird geprüft, ob das Verzeichnis vorhanden und beschreibbar ist.

in_array() {
        local i
        needle=$1
        if [ -z "$1" ]; then return 0; fi
        for i in $2
        do
                if [ "$i" == "$needle" ]; then return 1; fi
        done
        return 0
}
for SCSI_DEVBUSNR in $SCSI_DEVBUS
do 
        SCSI_DEVNAME=`dmesg | grep -m 1 "sd $SCSI_DEVBUSNR.*\[" | awk -F[ '{print $NF}' | awk -F] '{print $1}'`
        USB_MOUNT=`mount | grep -m 1 "$SCSI_DEVNAME" | awk '{ print $3 }'`
        if [ $USB_MOUNT ]; then
                echo "devices: ${#array[@]}"
                echo -e "USB-device $SCSI_DEVNAME found.";
                in_array $USB_MOUNT $array;
                if [ $? = 0 -a -d $USB_MOUNT -a -w $USB_MOUNT ]; then array=( "${array[@]}" $USB_MOUNT );fi
        fi
done

Auswahl

Hier gibt es zwei Ansätze. Die einfachste Art wird im Listing 1 gezeigt. Es liest die Eingabe des Laufwerks über die Tastatur ein, wobei das erste Element in der Laufwerksliste als Default gesetzt ist. Im Listing 2 wird eine quasi-grafische Ausgabe über dialog verwendet. Das ausgewählte Laufwerk wird nochmal auf Gültigkeit geprüft, da während der Laufzeit ja auch das USB-Laufwerk sich vom System abmelden kann bzw. durch den Benutzer abgemeldet wird.

Beispiel

Als Beispiel für die Verwendung des Scripts kann folgendes Codeschnippsel anstelle des Platzhalters in Listing 2 kommen. Es macht ein Backup eines ausgewählten Verzeichnisses auf das USB-Laufwerk.

source=$(dialog --shadow --backtitle "$0" --stdout --title "Quellverzeichnis auswählen" --dselect "$HOME/" 40 60)
if [ -d "$source" ]; then
        rsync -r --progress $source $device | unbuffer -p sed -u 's/\r/\n/g' | awk '/xfer#/ {sub(".*to-check=","");sub(")$","");split($0, p, "/");printf "%i%%\n", (1-p[1]/p[2])*100;fflush();}' _
         | grep --line-buffered '%' | sed -u 's/%//g' | dialog --shadow --backtitle "$0" --stdout --title "rsync progress" --gauge "Syncronisiere...\n\n$source -> $device" 10 70 0
fi

Ein fertiges Script ist zum Testen unter http://linux.singollo.de/copy2usb.sh zu finden.

Listing 1

#/bin/sh
 
in_array() {
        local i
        needle=$1
        if [ -z "$1" ]; then return 0; fi
        for i in $2
        do
                if [ "$i" == "$needle" ]; then return 1; fi
        done
        return 0
}
 
echo -e "Scanning output from dmesg...\n"
 
array=( )
SCSI_DEVBUS=`dmesg | awk -Fscsi '/scsi.*usb-storage/{ print $2 }' | awk '{ print $1 }'`
 
if [ -z $SCSI_DEVBUS ]; then
        SCSI_DEVBUS=`dmesg | awk '/usb-storage: device found at/{ print $NF-1 }'`
fi
 
for SCSI_DEVBUSNR in $SCSI_DEVBUS
do 
        SCSI_DEVNAME=`dmesg | grep -m 1 "sd $SCSI_DEVBUSNR.*\[" | awk -F[ '{print $NF}' | awk -F] '{print $1}'`
        USB_MOUNT=`mount | grep -m 1 "$SCSI_DEVNAME" | awk '{ print $3 }'`
        if [ $USB_MOUNT ]; then
                echo "devices: ${#array[@]}"
                echo -e "USB-device $SCSI_DEVNAME found.";
                in_array $USB_MOUNT $array;
                if [ $? = 0 -a -d $USB_MOUNT -a -w $USB_MOUNT ]; then array=( "${array[@]}" $USB_MOUNT );fi
        fi
done
 
if [ ${#array[@]} = 0 ]; then 
        echo -e "There is no USB-device.\n";
else
        echo -e "\nThis is a list of usable devices: ${array[@]}";
        device="${array[0]}"
        read -e -i "$device" -p "Please enter a device: " input
        device="${input:-$device}"
        in_array $device $array;
        if [  $? = 1 -a -d $device -a -w $device ]; then 
                echo -e "You selected: $device\n";
 
                # Ab hier steht das, was mit dem Gerät/Verzeichnis passieren soll...
                # Script sollte mit exit 0 beendet werden
 
        else
                echo -e "\nExit, because you are not definied a valid target device."; exit 1;
        fi
 
fi

Listing 2

#/bin/sh
 
in_array() {
        local i
        needle=$1
        if [ -z "$1" ]; then return 0; fi
        for i in $2
        do
                if [ "$i" == "$needle" ]; then return 1; fi
        done
        return 0
}
 
array=( )
devices=( )
SCSI_DEVBUS=`dmesg | awk -Fscsi '/scsi.*usb-storage/{ print $2 }' | awk '{ print $1 }'`
 
if [ -z $SCSI_DEVBUS ]; then
        SCSI_DEVBUS=`dmesg | awk '/usb-storage: device found at/{ print $NF-1 }'`
fi
 
for SCSI_DEVBUSNR in $SCSI_DEVBUS
do 
        SCSI_DEVNAME=`dmesg | grep -m 1 "sd $SCSI_DEVBUSNR.*\[" | awk -F[ '{print $NF}' | awk -F] '{print $1}'`
        USB_MOUNT=`mount | grep -m 1 "$SCSI_DEVNAME" | awk '{ print $3 }'`
        if [ $USB_MOUNT ]; then
                in_array $USB_MOUNT $array
                if [ $? = 0 -a -d $USB_MOUNT -a -w $USB_MOUNT ]; then array=( "${array[@]}" "$USB_MOUNT" );fi
        fi
done
 
IFS=$','
 
if [ ${#array[@]} = 0 ]; then 
        dialog --shadow --backtitle "$0" --stdout --title "Exit" --infobox "\n\nEs ist kein USB-Laufwerk gefunden worden." 7 45
        exit 1
else
        for i in ${array[@]}
        do
                devices=( "${devices[@]}" "$i,Laufwerk #${#devices[@]},off" )
        done
        device=$(dialog --shadow --backtitle "$0" --stdout --title "Auswahl" --radiolist "Ziellaufwerk auswählen" 15 55 6 ${devices[@]})
        in_array $device $array;
        if [  $? = 1 -a -d $device -a -w $device ]; then 
                question=$(dialog --shadow --backtitle "$0" --stdout --title "Info" --yesno "\nDas Quellverzeichnis wird nach $device gesichert.\n\nWollen Sie fortfahren?" 10 60)
                if [ $? = 1 ]; then exit 1; fi
 
                # Ab hier steht das, was mit dem Gerät/Verzeichnis passieren soll...
                # Script sollte mit exit 0 beendet werden
 
                dialog --shadow --backtitle "$0" --stdout --title "Ende" --infobox "\n\nFertig." 7 30
        else
                dialog --shadow --backtitle "$0" --stdout --title "Exit" --infobox "\n\nSie haben kein Ziellaufwerk ausgewählt." 7 43
                exit 1
        fi
fi
scripts/search_usb_storage_device.txt · Zuletzt geändert: 07.10.2012 18:31 (Externe Bearbeitung)