Fedora Online Forum

Il forum della comunità italiana di Fedora

#1 12-05-2016 13:42:11

punico
Pinguino avanzato
Registrato: 07-11-2012
Messaggi: 329

[Risolto] script bash per estrarre caratteri da una stringa

Mi ritrovo alle prese con uno script bash a cui vorrei affidare il compito di estrarre il nome di ciascuno dei filesystem presenti in un SD esterno
lo script, ancora incompleto, è:

#!/bin/sh
# Script 'TrovaDiskBkp.sh' di:
cd ~/mont/dativari/contabfam
Scelta="N"
clear
while [ "$Scelta" != "SI" ]		# diverso da
do
	echo "SPEGNI unità DISK esterna"
	echo "rispondi: 'SI' e"
	echo "-------- PREMI INVIO --------"
	read Scelta
	echo "hai risposto:" $Scelta
done
ls -l /dev/disk/by-uuid > /home/piero/Scrivania/locaz_partiz.txt
Scelta="N"
while [ "$Scelta" != "SI" ]
do
	echo "ACCENDI E CONNETTI ad una porta usb unità DISK esterna"
	echo "rispondi: 'SI' e"
	echo "---------------------- PREMI INVIO ---------------------"
	read Scelta
	echo "hai risposto:" $Scelta
done
ls -l /dev/disk/by-uuid >> /home/piero/Scrivania/locaz_partiz.txt
sort /home/piero/Scrivania/locaz_partiz.txt | uniq -u > /home/piero/Scrivania/partiz_bkp.txt
riga=
let ind=0
flnmpartiz=/home/piero/Scrivania/partiz_bkp.txt
declare -a TbRec		#dichiarazione di una tabella in memoria
while read riga
do
	TbRec[ind]="$riga"
	echo "Riga$ind: ${TbRec[ind]}"
	let "ind+=1"
done < $flnmpartiz
while [ "$ind" \< "$TotEle" ]
do
	echo
	echo "ind: $ind"
	echo "TbRecRiga$ind: ${TbRec[ind]}"
	let lungh=${#TbRec[ind]}
	echo "LunghRigaTbRec: $lungh"
	echo "expr"${TbRec[ind]}":’.*\(......\)’"
	let ind+=1
done

Lo script funziona bene tranne la riga

echo "expr"${TbRec[ind]}":’.*\(......\)’"

terz'ultima dello script.

Detta riga, secondo quanto riportato dalla guida  che ho consultato, avrebbe dovuto esporre a video soltanto gli ultimi 4 caratteri di ciscuna stringa esaminata, invece viene esposta tutta stringa all'interno della linea di comando, come se fosse un'unico valore.
Spero che qualcuno, conoscente del suo corretto funzionamento, mi possa aiutare ad operare la correzione adeguata per ottenere il risultato atteso.

Ultima modifica di punico (14-05-2016 21:10:18)


Ciao,
Petrus

Non in linea

#2 12-05-2016 14:19:33

punico
Pinguino avanzato
Registrato: 07-11-2012
Messaggi: 329

Re: [Risolto] script bash per estrarre caratteri da una stringa

Ho modificato la riga fatidica così:

echo `expr "${TbRec[ind]}" : ’.*\(.....\)’`

Come si può riscontrare col messaggio precedente ho aggiunto due apici ad andamento inverso, così come li riporta la guida. Ho rieseguito lòo scvript che continua a non funzionare, però questa volta mi ha dato come valore uno "0"(zero)

TbRecRiga3: lrwxrwxrwx. 1 root root 10 11 mag 16.04 ebd0f1e4-3b6b-4cfb-aedf-511f03e62d98 -> ../../sdh1
0


Ciao,
Petrus

Non in linea

#3 12-05-2016 20:16:34

arkanoid
Moderatore
Da Trento
Registrato: 06-05-2010
Messaggi: 2'142
Sito web

Re: [Risolto] script bash per estrarre caratteri da una stringa

Ho spostato il topic in una categoria più consona.

Per estrarre gli ultimi 4 caratteri, ti può bastare una cosa di questo genere:

word="punico"
echo ${word: -4}

Nel tuo caso quindi, qualcosa come:

echo ${TbRec[ind]: -4}

Ho provato il tuo script. Togliendo l'opzione "-u" a riga 25, sembra funzionare, ma non mi entra nell'ultimo ciclo while.

Non sono un esperto di bash (tutt'altro). Ti consiglierei comunque, se posso, di verificare (almeno) quanto segue:
1) La variabile "TotEle", da dove viene? Non è valorizzata.
2) Se usi "ind" come indice anche nell'ultimo while, immagino tu debba rifarla partire da 0.
3) Il "<", funge da operatore di comparazione per stringe ASCII. Probabilmente, volevi scrivere qualcosa del tipo:

while [ "$ind" -lt "$TotEle" ]

4) Almeno concettualmente, credo che un ciclo for sarebbe più indicato


|| FAS: juliuxpigface || IRC: jpigface || GITHUB: pigjuliux ||

Non in linea

#4 12-05-2016 21:23:38

punico
Pinguino avanzato
Registrato: 07-11-2012
Messaggi: 329

Re: [Risolto] script bash per estrarre caratteri da una stringa

L'istruzione che mi hai proposta

echo ${TbRec[ind]: -4}

funziona perfettamente

Io avevo risolto nel tardo pomeriggio con le seguenti

let lungh=${#TbRec[ind]}
let pos=lungh-3
echo `expr substr "${TbRec[ind]}" $pos 4`

Rispondo ora alle tue ulteriori riflessioni.

1) La variabile "TotEle", da dove viene? Non è valorizzata.

Sicuramente, nel copia incolla, ho eliminato la riga relativa alla sua valorizzazione perché era interposta ad alcune righe di commento. La sua generazione è così ottenuta:

declare -a TbRec		#dichiarazione di una tabella in memoria
while read riga
do
	TbRec[ind]="$riga"
	echo "Riga$ind: ${TbRec[ind]}"
	let "ind+=1"
done < $flnmpartiz
let ind=0
echo "ind: $ind"
let TotEle=${#TbRec[*]}
echo "TotEle= $TotEle"
while [ "$ind" \< "$TotEle" ]
do
.... bla...bla...bla
done

2) Se usi "ind" come indice anche nell'ultimo while, immagino tu debba rifarla partire da 0.

Si, anche detta riga è stata eliminata, nell'esposizione del problema, per errore

3) Il "<", funge da operatore di comparazione per stringe ASCII Probabilmente, volevi scrivere qualcosa del tipo:

while [ "$ind" -lt "$TotEle" ]

io ho usato

while [ "$ind" \< "$TotEle" ]

che per me é più facilmente comprensibile e funziona ugualmente.

4) Almeno concettualmente, credo che un ciclo for sarebbe più indicato

Sono d'accordo, ma ho incontrato difficoltà ad applicarlo. Ho allora deciso ad impiegare il ciclo while.

Considera che non sono pratico nello scrivere script bash. Mi capita solo ogni tanto di tirar fuori qualcosa per le mie più che modeste necessità. Allora mi do da fare a scrivere del codice, quasi sempre con l'aiuto delle guide disponibili sul web.

Il mio approccio attuale mi serve per completare il lavoro, iniziato mesi fa, relativamente al salvataggio dei miei dati e con lo script che sto costruendo adesso penso di trasferire settimanalmente in maniera semiautomatica su un disco esterno i tre file system utilizzati a turno, giornalmente, sul secondo disco interno per l'operazione di salvataggio.
Non appena avrò finito, con la relativa esecuzione a regime dello script in costruzione, pubblicherò tutto lo script nella discussione corrente

Grazie per il tuo grosso aiuto e se dovessi ritenere di dovermi suggerire ulteriori correzioni, sappi che saranno sempre ben accette.


Ciao,
Petrus

Non in linea

#5 14-05-2016 12:40:50

frafra
Amministratore
Da Trondheim (Norvegia)
Registrato: 14-05-2014
Messaggi: 2'358
Sito web

Re: [Risolto] script bash per estrarre caratteri da una stringa

Non ho capito bene cosa tu voglia ottenere, ma siccome si parla di Bash, voglio partecipare al banchetto big_smile

Singolo dispositivo

Se vuoi vedere i label delle partizioni del dispositivo che stai per inserire, si può utilizzare udevadm monitor. Di solito utilizzerei inotifywait, ma su finti filesystem (come devfs) spesso non funziona.

Ecco il mio comando:

$ stdbuf -oL udevadm monitor --subsystem-match=block | grep -m1 ' add ' | grep -Po '(?<=block/)\S*' | xargs -I~ sh -c "sleep 1; lsblk -o name,label /dev/~"

Mentre preferisci utilizzare sed+head al posto del doppio grep il comando risulta essere un po' più breve (perché si può togliere una opzione da udevadm):

$ stdbuf -oL udevadm monitor | stdbuf -oL sed -nr 's:.* add .*block/(\S*).*:\1:p' | head -n1 | xargs -I~ sh -c "sleep 1; lsblk -o name,label /dev/~"

Credo si possa far collassare sed+head in un unico comando sed (ovvero, fai la prima sostituzione, mostra il risultato ed esci).
La cosa che non mi piace è lo sleep 1 che ho dovuto inserire. Non saprei come rimuoverlo, dato che non so quando effettivamente udev abbia finito di leggere tutti i label delle varie partizioni. Servirebbe una condizione per terminare senza ritardo.

Dispositivi multipli

Se invece volessi vedere in tempo reale, con un flusso continuo, si può utilizzare udiskctl monitor:

$ udisksctl monitor | grep -o '/dev/disk/by-label/.*'

E se volessi vedere anche a quale dispositivo puntano...

$ udisksctl monitor | stdbuf -oL grep -o '/dev/disk/by-label/.*' | xargs -L1 ls -l

Possibili sviluppi

Ci sono comandi interessanti che potrebbero tornare utili, tipo symlink, timeout, ... Tutto dipende da cosa tu voglia fare smile

Non in linea

#6 14-05-2016 14:06:02

frafra
Amministratore
Da Trondheim (Norvegia)
Registrato: 14-05-2014
Messaggi: 2'358
Sito web

Re: [Risolto] script bash per estrarre caratteri da una stringa

dmesg dice quali siano le partizioni presenti, quindi una possibile soluzione al problema della terminazione c'è:

$ journalctl --system --reverse | grep -Pm1 "(?<= kernel:  $device: ).*"

Segue script particolarmente prolisso (aggiornamento: l'ho reso più breve):

parts=(); while read -r line
do
    [[ $line != *' add '* ]] && continue
    if [ ${#parts[@]} == 0 ]
    then
        dev=$(echo $line | grep -Po '(?<=block/)\S*')
        parts=($(journalctl -kr | grep -Pom1 "(?<=:  $dev: ).*"))
    else
        part=$(echo $line | grep -Po "(?<=block/$dev/)\S*")
        parts=( ${parts[@]/$part} )
        [ ${#parts[@]} == 0 ] && break
    fi
done < <(stdbuf -oL udevadm monitor -us block)
lsblk -f /dev/$dev

Il fatto per me interessante è che questo script fa una funzione molto utile: non solo vede il dispositivo appena inserito, ma aspetta che udev faccia il suo corso, così che dopo si possano eseguire azioni come accedere alle partizioni del dispositivo via /dev/vattelapesca ecc.

Probabilmente esisterà un modo più immediato, ma non lo conosco sad

Ultima modifica di frafra (14-05-2016 14:44:21)

Non in linea

#7 14-05-2016 21:09:12

punico
Pinguino avanzato
Registrato: 07-11-2012
Messaggi: 329

Re: [Risolto] script bash per estrarre caratteri da una stringa

Grazie frafra per i tuoi suggerimenti, ma quando ho letto, le tue risposte avevo già  definito lo script che mi necessitava per determinare automaticamente i nomi degli sd attributi dal sistema alle partizioni del disco esterno in cui eseguire il cp dei tre file dati contenuti in altrettante partizioni del secondo disco interno in cui risiedono i salvataggi giornalieri dei miei dati originari.
La procedura che ho messo su alcuni mesi or sono prevede un salvataggio giornailero su una partizione fra le tre create sul 2° disco interno del pc. Alternativamente eseguo un cp su sdc2 (1° giorno), sdc3 (2* giorno) e sdc4 (3* giorno). Il ciclo ricomincia da sdc2 il 4° giorno. Poi settimanalmente, solitamente il Venerdì sera, eseguo un cp di sdc2, sdc3 e sdc4 in altrettanti sd di un disco esterno che connetto per l'occasione al pc, tramite una delle 4 porte usb presenti sul frontale del tower del pc, però poichè detti sd non prendono sempre lo stesso nome (a volte sdh1/2/3, a volte sdi1/2/3, a volte sdg1/2/3) il mio vecchio script non funzionava sempre. Mi serviva perciò individuare automaticamente il nome attribuito dal sistema al momento della connessione dell'unità esterna al pc.
Ho allora modificato opportunamente lo script, in base alle mie modeste conoscenze dell'ambiente bash, e lo script che ho messo a regime oggi e così redatto:

#!/bin/bash
# l'opzione -x permette di vedere ogni singolo comando eseguito dalla bash 
# Script 'bkpsettim.sh' per:
# avvio automatico della mia procedura bash di
# ------------------------ SALVATAGGIO SETTIMANALE DATI ------------------------
# data fine script: 14-mag-2016
#set -x      # Visualizza tutti i comandi prima della loro esecuzione.
clear
echo "SALVATAGGIO SETTIMANALE DATI"
#
cd ~/mont/dativari/contabfam
Scelta="N"
while [ "$Scelta" != "SI" ]		# diverso da
do
	echo "SPEGNI unità DISK esterna"
	echo "rispondi: 'SI' e"
	echo "-------- PREMI INVIO --------"
	read Scelta
	echo "hai risposto: $Scelta"
	echo
done
ls -l /dev/disk/by-uuid > /home/piero/Scrivania/locaz_partiz.txt
Scelta="N"
while [ "$Scelta" != "SI" ]
do
	echo "ACCENDI E CONNETTI ad una porta usb unità DISK esterna"
	echo "rispondi: 'SI' e"
	echo "---------------------- PREMI INVIO ---------------------"
	read Scelta
	echo "hai risposto: $Scelta"
	echo
done
ls -l /dev/disk/by-uuid >> /home/piero/Scrivania/locaz_partiz.txt
sort /home/piero/Scrivania/locaz_partiz.txt | uniq -u > /home/piero/Scrivania/partiz_bkp.txt
riga=
let ind=0
flnmpartiz=/home/piero/Scrivania/partiz_bkp.txt
declare -a TbRec		#dichiarazione di una tabella in memoria
while read riga
do
	TbRec[ind]="$riga"
	let "ind+=1"
done < $flnmpartiz
let ind=0
let TotEle=${#TbRec[*]}
while [ "$ind" \< "$TotEle" ]
do
	let lungh=${#TbRec[ind]}
	let pos=lungh-3
#	echo `expr "${TbRec[ind]}" : '\(....\)'`		#  ESTRAZIONE DEI PRIMI 4 CRT --> funziona
	Strimia=`expr substr "${TbRec[ind]}" $pos 4`		#  ESTRAZIONE DI 4 crt, a partire dal 87/mo --> funziona
	TbRec[ind]=$Strimia
	let ind+=1
done
#---------------------------- odinamento TbRec ------------------------------------------
let ind=0
let fine="0"
scamb="n"
let UltElem=$TotEle-1
while [ $fine = "0" ]
do
	if [[ ${TbRec[ind]} > ${TbRec[ind+1]} ]]
	then
		Strimia=${TbRec[ind]}
		TbRec[ind]=${TbRec[ind+1]}
		TbRec[ind+1]=$Strimia
		scamb="s"
	fi
	let ind+=1
	if [[ $ind = $UltElem ]]
	then
		if [[ $scamb = "s" ]]
		then
			let scamb="n"
			let ind=0
		else
			let fine="1"	
		fi
	fi
done
#------------------------- Fine ordinamento ----------------------------
#let ind=0
#for ((ind=0;ind\<"$TotEle";++ind))
#do
#	echo
#	echo "TbRecRiga$ind: ${TbRec[ind]}"
#done
#
let ind=0
sdInput=
echo "AVVIO salvataggio di: sdc2 -> ${TbRec[ind]}"
echo "                      sdc3 -> ${TbRec[ind+1]}"
echo "                      sdc4 -> ${TbRec[ind+2]}"
echo "digita: 'SI' per PROSEGUIRE"
echo "        'N'  per USCIRE" 
read Scelta
echo "hai digitato: $Scelta"
if [[ $Scelta != "SI" ]]; then
	exit 0
fi
OraIni=$(date +%H)
MinutIni=$(date +%M)
for ((ii=2;ii\<5;++ii))
do
	sudo mount /dev/"sdc$ii" /home/piero/devinput
#	ls  /home/piero/devinput
	for nomefile in /home/piero/devinput/*
	do
		strimia="${nomefile##*/}"
		if [[ "${strimia: 0: 8}" == "dativari" ]]; then
			strimia1="/home/piero/devinput/$strimia"
			strimia2="/home/piero/mntpartbkp/$strimia"
			sudo mount /dev/${TbRec[ind]} /home/piero/mntpartbkp
#			ls  /home/piero/mntpartbkp
			echo "-----------------------------------------------------------------------------"
			echo
			echo "Inizio salvataggio di: sdc$ii -> ${TbRec[ind]} - ore: $(date +%k:%M)"
			echo "dal file: $strimia1"
			echo "al file: $strimia2"
			sudo rm -rf /home/piero/mntpartbkp/dativari*
#			ls  /home/piero/mntpartbkp
			sudo cp -a /home/piero/devinput/$strimia /home/piero/mntpartbkp/$strimia
			sudo umount /home/piero/devinput
			sudo umount /home/piero/mntpartbkp
			OraFin=$(date +%H)
			MinutFin=$(date +%M)
			echo "FINE salvataggio - ore: $OraFin:$MinutFin"
			let ind+=1		
		fi
	done
done
echo
if [[ $OraFin == 0 ]]; then
	OraFin=24
fi
if [[ $MinutFin < $MinutIni ]]; then
	let OraFin-=1
	let MinutFin+=60
fi	
let DuraMin=$MinutFin-$MinutIni
let DuraOre=$OraFin-$OraIni
echo "Durata complessiva del salvataggio: $DuraOre:$DuraMin"		
echo "-----------------------------------------------------------------------------"
exit 0

Lo script ora funziona perfettamente e svolge il suo lavoro in poco più di 30'.
Sono perciò pienamente soddisfatto del risultato che sono riuscito ad ottenere, grazie anche all'aiuto ricevuto da arkanoid.
Come avevo promesso ho pubblicato qui lo script.
Grazie ancora a tutti e due.


Ciao,
Petrus

Non in linea

#8 14-05-2016 22:00:19

frafra
Amministratore
Da Trondheim (Norvegia)
Registrato: 14-05-2014
Messaggi: 2'358
Sito web

Re: [Risolto] script bash per estrarre caratteri da una stringa

Figurati punico, è sempre un piacere.
Valuta anche rsync e simili per i backup.

Non in linea

Piè di pagina