[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, è:

[code]
#!/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[/code]

Lo script funziona bene tranne la riga

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

terz’ultima dello script.

Detta riga, secondo quanto riportato dalla http://www.tldp.org/LDP/abs/html/string-manipulation.html 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.

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)

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" ]
  1. Almeno concettualmente, credo che un ciclo for sarebbe più indicato

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.

[quote]

  1. La variabile “TotEle”, da dove viene? Non è valorizzata.[/quote]
    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

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

io ho usato while "$ind" \< "$TotEle" ] che per me é più facilmente comprensibile e funziona ugualmente.

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.

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

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 :slight_smile:

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 :frowning:

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:

[code]
#!/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[/code]

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.

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