[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home] [volume] [parte]


Capitolo 163.   Raccolta di funzioni per una shell POSIX

In questo capitolo viene proposta una raccolta di funzioni per una shell POSIX comune, allo scopo di facilitare la gestione di un sistema GNU/Linux. Queste funzioni derivano dall'esperienza di nanoLinux (una distribuzione GNU/Linux per architettura x86, derivata da Debian, descritta nel volume XXXII).

Queste funzioni si avvalgono evidentemente di programmi di servizio comuni nei sistemi Unix; in particolare SED (capitolo 667). Evidentemente, la comprensione del funzionamento di queste funzioni richiede una buona conoscenza nell'uso di tali programmi.

163.1   Estrapola da «/etc/passwd» le righe di un certo intervallo di numeri UID

password_records_by_uid_range uid_min uid_max < /etc/passwd > output_file
passwd_records_by_uid_range () {
    local UID_MIN="$1"
    local UID_MAX="$2"
    local RECORD=""
    local USER_ID=""
    #
    while read -r RECORD
    do
        USER_ID=`echo $RECORD | sed "s/^[^:]*:[^:]*:\([0-9]*\):.*/\1/"`
        #
        if [ 0$USER_ID -ge $UID_MIN ] && [ 0$USER_ID -le $UID_MAX ]
        then
            echo $RECORD
        fi
    done
}

L'esempio seguente utilizza la funzione per estrapolare le righe di /etc/passwd associate a numeri UID tra 1 000 e 29 999. Il risultato viene emesso semplicemente attraverso lo standard output.

cat /etc/passwd | passwd_records_by_uid_range 1000 29999 

163.2   Estrapola da «/etc/passwd» le righe di un certo intervallo di numeri GID

password_records_by_gid_range gid_min gid_max < /etc/passwd > output_file
passwd_records_by_gid_range () {
    local GID_MIN="$1"
    local GID_MAX="$2"
    local RECORD=""
    local GROUP_ID=""
    #
    while read -r RECORD
    do
        GROUP_ID=`echo $RECORD | sed "s/^[^:]*:[^:]*:[^:]*:\([0-9]*\):.*/\1/"`
        #
        if [ 0$GROUP_ID -ge $GID_MIN ] && [ 0$GROUP_ID -le $GID_MAX ]
        then
            echo $RECORD
        fi
    done
}

L'esempio seguente utilizza la funzione per estrapolare le righe di /etc/passwd associate al numero GID zero.

cat /etc/passwd | passwd_records_by_gid_range 0 0 

163.3   Estrapola da «/etc/group» le righe di un certo intervallo di numeri GID

group_records_by_gid_range gid_min gid_max < /etc/group > output_file
group_records_by_gid_range () {
    local GID_MIN="$1"
    local GID_MAX="$2"
    local RECORD=""
    local GROUP_ID=""
    #
    while read -r RECORD
    do
        GROUP_ID=`echo $RECORD | sed "s/^[^:]*:[^:]*:\([0-9]*\):.*/\1/"`
        #
        if [ 0$GROUP_ID -ge $GID_MIN ] && [ 0$GROUP_ID -le $GID_MAX ]
        then
            echo $RECORD
        fi
    done
}

L'esempio seguente utilizza la funzione per estrapolare le righe di /etc/group associate ai numeri GID da 1 000 a 29 999.

cat /etc/group | group_records_by_gid_range 1000 29999 

163.4   Seleziona un utente interattivamente

Per selezionare interattivamente l'utente si usa Dialog e si preferisce depositare il nome scelto in un file:

select_user uid_min uid_max output_file < /etc/passwd
select_user_menu () {
    #
    local UID_MIN="$1"
    local UID_MAX="$2"
    local RECORD=""
    local USER_ID=""
    #
    local USER_AND_HOME_LIST=""
    #
    while read -r RECORD
    do
        USER_ID=`echo $RECORD | sed "s/^[^:]*:[^:]*:\([0-9]*\):.*/\1/"`
        #
        if [ 0$USER_ID -ge $UID_MIN ] && [ 0$USER_ID -le $UID_MAX ]
        then
            echo $RECORD | sed "s/^\(.*\):.*:.*:.*:.*:\(.*\):.*/\1 \2/g"
        fi
    done
}
select_user () {
    #
    local UID_MIN="$1"
    local UID_MAX="$2"
    local INPUT_FILE="$3"
    local OUTPUT_FILE="$4"
    #
    local USER_AND_HOME_LIST=""
    local SELECTED_USER=""
    #
    local TEMPORARY=`tempfile`
    echo -n > $TEMPORARY
    #
    USER_AND_HOME_LIST=`cat $INPUT_FILE \
                        | select_user_menu $UID_MIN $UID_MAX \
                        | sort -u`
    #
    if dialog                           \
        --clear                         \
        --title "Users"                 \
        --menu  "Select a user name."   \
        0 0 0                           \
        $USER_AND_HOME_LIST             \
        "!EXIT!" "."                    \
        2> $TEMPORARY
    then
        SELECTED_USER=`cat $TEMPORARY`
        echo "" > $TEMPORARY
        #
        if [ "$SELECTED_USER" = "!EXIT!" ]
        then
            SELECTED_USER=""
        fi
    else
        SELECTED_USER=""
    fi
    #
    echo "$SELECTED_USER" > "$OUTPUT_FILE"
    rm $TEMPORARY
}

L'esempio seguente utilizza la funzione per estrapolare un utente dal file /etc/passwd, tra i numeri UID 1 000 e 29 999. Il risultato viene visualizzato attraverso lo standard output.

cat /etc/passwd | select_user 1000 29999 /etc/passwd /tmp/ciao
cat /tmp/ciao

163.5   Seleziona un campo di una certa riga da un file come «/etc/passwd», «/etc/group» e simili

table_get_column_field file indice n_colonna 
table_get_column_field () {
    #
    local INPUT_FILE="$1"
    local INDEX="$2"
    local COLUMN="$3"
    local RECORD=""
    local FIELD=""
    #
    if [ ! -r "$INPUT_FILE" ]
    then
        echo 1>&2 "[$0] cannot read \"$INPUT_FILE\"."
        return
    fi
    #
    if   [ "$COLUMN" = "1" ]
    then
        echo 1>&2 "[$0] index should be more than one."
        echo "$INDEX"
        return
    fi
    #
    RECORD=`grep -m 1 "^${INDEX}:" "$INPUT_FILE"`
    #
    if [ "$RECORD" = "" ]
    then
        echo 1>&2 "[$0] index \"$INDEX\" not found inside \"$INPUT_FILE\"."
        return
    fi
    #
    while [ "$COLUMN" -gt "1" ]
    do
        RECORD=`echo $RECORD | sed "s/^[^:]*://"`
        COLUMN=$(($COLUMN - 1))
    done
    #
    FIELD=`echo $RECORD | sed "s/^\([^:]*\).*$/\1/"`
    #
    echo $FIELD
    return
}

L'esempio seguente utilizza la funzione per estrapolare la parola d'ordine cifrata dell'utente «tizio» dal file /etc/passwd, visualizzando il risultato attraverso lo standard output.

table_get_column_field /etc/shadow tizio 2

163.6   Aggiunge un utente Unix e Samba, simultaneamente

user_add_unix_samba user passwd home full_name room work_ph home_ph

La funzione che viene proposta è estrapolata dallo script nanorc di nanoLinux, dove però viene eseguita un'attività più complessa. Questa edizione particolare non è collaudata ed è qui solo come promemoria.

user_add_unix_samba () {
    #
    local NEW_USER="$1"
    local NEW_PASSWD="$2"
    local NEW_HOME="$3"
    local NEW_FULL_NAME="$4"
    local NEW_ROOM="$5"
    local NEW_WORK_PHONE="$6"
    local NEW_HOME_PHONE="$7"
    local TEMP_USER=""
    #
    # Elimina le virgole e altri caratteri inopportuni
    # dalle informazioni dell'utente.
    #
    NEW_FULL_NAME=`echo $NEW_FULL_NAME   | sed "/,:=/ /g"`
    NEW_ROOM=`echo $NEW_ROOM             | sed "/,:=/ /g"`
    NEW_WORK_PHONE=`echo $NEW_WORK_PHONE | sed "/,:=/ /g"`
    NEW_HOME_PHONE=`echo $NEW_HOME_PHONE | sed "/,:=/ /g"`
    #
    # Verifica che i dati siano validi.
    #
    if   [ "$NEW_USER" = "" ]
    then
        echo 1>&1 "[$0] cannot create user with no name."
        false
        return
    elif echo "$NEW_USER" | grep "[^a-z0-9]" > "/dev/null"
    then
        echo 1>&1 "[$0] user name must contain only lower case letters and numbers."
        false
        return
    fi
    #
    # Il nominativo utente non deve superare i 16 caratteri
    #
    TEMP_USER=`echo $NEW_USER | sed "s/^\(................\).*$/\1/"`
    if [ "$TEMP_USER" = "$NEW_USER" ]
    then
        true
    else
        echo 1>&2 "[$0] user name cannot be longer than 16 characters."
        false
        return
    fi
    #
    # Controlla la directory personale
    #
    if [ "$NEW_HOME" = "" ]
    then
        NEW_HOME="/home/$NEW_USER"
    fi
    #
    if [ -e "$NEW_HOME" ]
    then
        echo 1>&2 "[$0] home directory \"$NEW_HOME\" cannot be created."
        false
        return
    fi
    #
    # Controlla che ci sia la parola d'ordine.
    #
    if [ "$NEW_PASSWD" = "" ]
    then
        echo 1>&2 "[$0] the new user must have a password."
        false
        return
    fi
    #
    # Crea l'utente, usando inizialmente lo script "adduser",
    # secondo le convenzioni Debian.
    #
    if adduser \
        --disabled-password \
        --no-create-home \
        --home "$NEW_HOME" \
        --gecos "$NEW_FULLNAME,$NEW_ROOM,$NEW_WORK_PHONE,$NEW_HOME_PHONE"\
        $NEW_USER
    then
        #
        # Crea manualmente la directory personale.
        #
        mkdir -p "$NEW_HOME"
        rmdir    "$NEW_HOME"
        #
        cp -dpR  /etc/skel  "$NEW_HOME"
        chown -R $NEW_USER: "$NEW_HOME"
        chmod 0755          "$NEW_HOME"
        #
        # Aggiunge un collegamento simbolico all'interno di
        # "/var/mail/", perché alcuni programmi ne hanno bisogno.
        # Si presume che il file "~/mail/mbox" venga usato per
        # accumulare i messaggi di posta elettronica dell'utente.
        #
        rm "/var/mail/$NEW_USER" 2> "/dev/null"
        ln -s "../../$NEW_HOME/mail/mbox" "/var/mail/$NEW_USER"
        #
        # Attribuisce la parola d'ordine (nel farlo, crea anche
        # l'utenza per Samba).
        #
        if user_passwd_unix_samba "$NEW_USER" "$NEW_PASSWD"
        then
            true
        else
            #
            # Elimina l'utenza.
            #
            user_del_unix_samba "$NEW_USER"
            false
            return
    else
        echo 1>&2 "[$0] cannot create user \"$NEW_USER\"."
        false
        return
    fi
    #
    rm $TEMPORARY
}

163.7   Cambia la parola d'ordine a un utente Unix e Samba, simultaneamente

user_passwd_unix_samba user passwd
user_passwd_unix_samba () {
    #
    local OLD_USER="$1"
    local NEW_PASSWD="$2"
    #
    local TEMPORARY=`tempfile`
    echo -n > $TEMPORARY
    #
    # Tenta di eliminare un'utenza vecchia, con lo stesso nome
    # dalla gestione di Samba.
    #
    smbpasswd -x $OLD_USER 2> "/dev/null" 1> "/dev/null" 
    #
    # Imposta la parola d'ordine Unix.
    #
    if ( sleep 1 ; echo $NEW_PASSWD ; sleep 1 ; echo $NEW_PASSWD ) \
       | /usr/bin/passwd $NEW_USER 2> $TEMPORARY
    then
        #
        # Crea o ricrea l'utenza Samba.
        #
        if [ -x /usr/bin/smbpasswd ]
        then
            if ( sleep 1 ; echo $NEW_USER ; sleep 1 ; echo $NEW_USER ) \
               | /usr/bin/smbpasswd -s -a $NEW_USER 2> $TEMPORARY
            then
                true
            else
                echo 1>&2 "[$0] problem changing Samba password to user \
"$NEW_USER": `cat $TEMPORARY`."
                rm "$TEMPORARY"
                false
                return
            fi
        fi
    else
        echo 1>&2 "[$0] problem changing Unix password to user \
"$NEW_USER": `cat $TEMPORARY`."
        rm "$TEMPORARY"
        false
        return
    fi
    rm "$TEMPORARY"
}

163.8   Elimina un utente Unix e Samba, simultaneamente

user_del_unix_samba user
user_del_unix_samba () {
    #
    local OLD_USER="$1"
    local OLD_HOME=""
    #
    # Trova la directory personale.
    #
    OLD_HOME=`cat /etc/passwd \
              | grep "^$OLD_USER:" \
              | sed "s/^[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\):[^:]*$/\1/"`
    #
    # Elimina l'utenza dalla gestione di Samba.
    #
    if [ -x /usr/bin/smbpasswd ]
    then
        if smbpasswd -x $OLD_USER
        then
            true
        else
            echo 1>&2 "[$0] cannot remove Samba user \"$OLD_USER\"."
        fi
    fi
    #
    # Elimina l'utenza Unix.
    #
    if deluser --remove-home "$OLD_USER"
    then
        #
        # Elimina il collegamento simbolico in "/var/mail".
        #
        rm "/var/mail/$OLD_USER"
    else
        echo 1>&2 "[$0] cannot remove Unix user \"$OLD_USER\"."
        false
        return
    fi
}

163.9   Seleziona interattivamente salvando la selezione

La funzione che viene proposta richiede due elenchi, contenuti in altrettanti file di testo, contenenti rispettivamente un insieme di voci e un sottoinsieme di voci da selezionare. Lo scopo è quello di cambiare il sottoinsieme selezionato e di aggiornare il contenuto del secondo file.

select_subset_save file_list_full file_list_selected\
  \                   selection_description unselection_description\
  \                   selection_word\
  \                   list_header list_description\
  \                   file_return_value_selected\
  \                   file_return_value_unselected

La funzione si avvale di un'altra che deve produrre l'opposto del sottoinsieme selezionato:

selection_invert ()
{
    local LIST_FULL="$1"
    local LIST_SELECTED="$2"
    local LIST_UNSELECTED=""
    local l=""
    local s=""
    local ITEM_FOUND=""
    #
    if [ "$LIST_SELECTED" = "" ]
    then
        LIST_UNSELECTED="$LIST_FULL"
    else
        for l in $LIST_FULL
        do
            ITEM_FOUND="0"
            for s in $LIST_SELECTED
            do
                if [ "$l" = "$s" ]
                then
                    ITEM_FOUND="1"
                    break
                fi
            done
            if [ "$ITEM_FOUND" = "1" ]
            then
                true
            else
                LIST_UNSELECTED="$LIST_UNSELECTED $l"
            fi
        done
    fi
    #
    echo "$LIST_UNSELECTED"
}
select_subset_save ()
{
    local FILE_LIST_FULL="$1"
    local FILE_LIST_SELECTED="$2"
    local SELECTION_DESCRIPTION="$3"
    local UNSELECTION_DESCRIPTION="$4"
    local SELECTION_WORD="$5"
    local LIST_HEADER="$6"
    local LIST_DESCRIPTION="$7"
    local FILE_RETURN_VALUE_SELECTED="$8"
    local FILE_RETURN_VALUE_UNSELECTED="$9"
    #    
    local LIST_FULL=""
    local LIST_SELECTED=""
    local LIST_SELECTED_VALID=""
    local LIST_UNSELECTED=""
    local ITEM_FOUND=""
    local ITEM_SELECTION=""
    local SELECTION=""
    local SELECTED=""
    #
    local TEMPORARY=`tempfile`
    touch $TEMPORARY
    #
    LIST_FULL=`cat $FILE_LIST_FULL | sed "s/#.*$//" 2> /dev/null`
    LIST_SELECTED=`cat $FILE_LIST_SELECTED | sed "s/#.*$//" 2> /dev/null`
    #
    if [ "$LIST_FULL" = "" ]
    then
        dialog --msgbox "The file \"$FILE_LIST_FULL\" is empty!" 0 0
        false
        return
    fi
    #
    # Unselected and select again.
    #
    LIST_UNSELECTED=`selection_invert "$LIST_FULL" "$LIST_SELECTED"`
    LIST_SELECTED=`selection_invert "$LIST_FULL" "$LIST_UNSELECTED"`
    #
    # Item selection list.
    #
    for i in $LIST_SELECTED
    do
        ITEM_SELECTION="$ITEM_SELECTION $i ${SELECTION_WORD}_$i on"
    done
    for i in $LIST_UNSELECTED
    do
        ITEM_SELECTION="$ITEM_SELECTION $i ${SELECTION_WORD}_$i off"
    done
    #
    # Dialog.
    #
    while true
    do
        if dialog  \
            --clear \
            --title "$LIST_HEADER" \
            --checklist "$LIST_DESCRIPTION" \
            0 0 0 \
            "!ALL!"  "$SELECTION_DESCRIPTION" off     \
            "!NONE!"  "$UNSELECTION_DESCRIPTION" off   \
            $ITEM_SELECTION   \
            2> $TEMPORARY
        then
            SELECTION=`cat $TEMPORARY`
            echo "" > $TEMPORARY
            #
            # Do something.
            #
            if   echo "$SELECTION" | grep "!ALL!" > "/dev/null"
            then
                ITEM_SELECTION=""
                for i in $LIST_FULL
                do
                    ITEM_SELECTION="$ITEM_SELECTION $i ${SELECTION_WORD}_$i on"
                done
                continue
                #
            elif   echo "$SELECTION" | grep "!NONE!" > "/dev/null"
            then
                ITEM_SELECTION=""
                for i in $LIST_FULL
                do
                    ITEM_SELECTION="$ITEM_SELECTION $i ${SELECTION_WORD}_$i off"
                done
                continue
                #
            else
                LIST_SELECTED=""
                for s in $SELECTION
                do
                    #
                    # Remove the double quotes.
                    #
                    SELECTED=`echo $s | sed "s/\"//g"`
                    LIST_SELECTED="$LIST_SELECTED $SELECTED"
                    #
                done
                #
                LIST_UNSELECTED=`selection_invert "$LIST_FULL" "$LIST_SELECTED"`
                #
                # Save data.
                #
                echo -n > "$FILE_LIST_SELECTED"
                for s in $LIST_SELECTED
                do
                    echo "$s" >> "$FILE_LIST_SELECTED"
                done
                #
                # Stop the loop.
                #
                break
                #
            fi
        else
            false
            return
        fi
    done    
    #
    rm -f $TEMPORARY
    #
    echo -n > "$FILE_RETURN_VALUE_SELECTED"
    for s in $LIST_SELECTED
    do
        echo "$s" >> "$FILE_RETURN_VALUE_SELECTED"
    done
    #   
    echo -n > "$FILE_RETURN_VALUE_UNSELECTED"
    for s in $LIST_UNSELECTED
    do
        echo "$s" >> "$FILE_RETURN_VALUE_UNSELECTED"
    done
    #
    true
    #
}

Si osservi l'esempio seguente:

select_subset_save  /etc/xxx/ELENCO_COMPLETO \
                    /etc/xxx/ELENCO_SELEZIONATO \
                    "selezione di tutte le voci" \
                    "rimozione di tutte le selezioni" \
                    "attiva" \
                    "attivazione voci" \
                    "Selezionare le voci da attivare:" \
                    /tmp/xxx.selezionati \
                    /tmp/xxx.non_selezionati

In questo c'è il file /etc/xxx/ELENCO_COMPLETO, contenente un elenco di voci (ogni voce deve costituire una parola sola), quindi c'è il file /etc/xxx/ELENCO_SELEZIONATO con un sottoinsieme delle voci del primo file. Attraverso la selezione che si esegue con la funzione, il file /etc/xxx/ELENCO_SELEZIONATO viene aggiornato con il nuovo sottoinsieme, ma in ogni caso si genera il file /tmp/xxx.selezionati con le voci selezionate e il file /tmp/xxx.non_selezionati con le altre voci.


Appunti di informatica libera 2008 --- Copyright © 2000-2008 Daniele Giacomini -- <appunti2 (ad) gmail·com>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome raccolta_di_funzioni_per_una_shell_posix.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home]

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory