colcmp.sh
Confronta le coppie nome / valore in 2 file nel formato name value\n
. Scrive name
a Output_file
se modificato. Richiede bash v4 + per array associativi .
uso
$ ./colcmp.sh File_1.txt File_2.txt
User3 changed from 'US' to 'NG'
no change: User1,User2
File di uscita
$ cat Output_File
User3 has changed
Fonte (colcmp.sh)
cmp -s "$1" "$2"
case "$?" in
0)
echo "" > Output_File
echo "files are identical"
;;
1)
echo "" > Output_File
cp "$1" ~/.colcmp.array1.tmp.sh
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.array1.tmp.sh
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.array1.tmp.sh
chmod 755 ~/.colcmp.array1.tmp.sh
declare -A A1
source ~/.colcmp.array1.tmp.sh
cp "$2" ~/.colcmp.array2.tmp.sh
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array2.tmp.sh
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.array2.tmp.sh
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A2\\[\\1\\]=\"\\2\"/" ~/.colcmp.array2.tmp.sh
chmod 755 ~/.colcmp.array2.tmp.sh
declare -A A2
source ~/.colcmp.array2.tmp.sh
USERSWHODIDNOTCHANGE=
for i in "${!A1[@]}"; do
if [ "${A2[$i]+x}" = "" ]; then
echo "$i was removed"
echo "$i has changed" > Output_File
fi
done
for i in "${!A2[@]}"; do
if [ "${A1[$i]+x}" = "" ]; then
echo "$i was added as '${A2[$i]}'"
echo "$i has changed" > Output_File
elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
echo "$i changed from '${A1[$i]}' to '${A2[$i]}'"
echo "$i has changed" > Output_File
else
if [ x$USERSWHODIDNOTCHANGE != x ]; then
USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
fi
USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
fi
done
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
;;
*)
echo "error: file not found, access denied, etc..."
echo "usage: ./colcmp.sh File_1.txt File_2.txt"
;;
esac
Spiegazione
Analisi del codice e del suo significato, per quanto ne so. Accolgo con favore modifiche e suggerimenti.
Confronto file di base
cmp -s "$1" "$2"
case "$?" in
0)
# match
;;
1)
# compare
;;
*)
# error
;;
esac
cmp imposterà il valore di $? come segue :
- 0 = i file corrispondono
- 1 = i file differiscono
- 2 = errore
Ho scelto di usare un'istruzione case .. esac per valutare $? perché il valore di $? cambia dopo ogni comando, incluso test ([).
In alternativa avrei potuto usare una variabile per contenere il valore di $? :
cmp -s "$1" "$2"
CMPRESULT=$?
if [ $CMPRESULT -eq 0 ]; then
# match
elif [ $CMPRESULT -eq 1 ]; then
# compare
else
# error
fi
Sopra fa la stessa cosa dell'affermazione case. IDK che mi piace di più.
Cancella l'output
echo "" > Output_File
Sopra cancella il file di output, quindi se nessun utente è cambiato, il file di output sarà vuoto.
Faccio questo all'interno delle dichiarazioni del caso in modo che Output_file rimanga invariato in caso di errore.
Copia il file utente in Shell Script
cp "$1" ~/.colcmp.arrays.tmp.sh
Sopra copia File_1.txt nella home directory dell'utente corrente.
Ad esempio, se l'utente corrente è john, quanto sopra sarebbe lo stesso di cp "File_1.txt" /home/john/.colcmp.arrays.tmp.sh
Fuga dai personaggi speciali
Fondamentalmente, sono paranoico. So che questi personaggi potrebbero avere un significato speciale o eseguire un programma esterno quando eseguiti in uno script come parte dell'assegnazione delle variabili:
- `- back-tick - esegue un programma e l'output come se l'output fosse parte del tuo script
- $ - simbolo del dollaro - di solito prefigura una variabile
- $ {} - consente una sostituzione variabile più complessa
- $ () - idk cosa fa ma penso che possa eseguire il codice
Quello che non so è quanto non so di bash. Non so quali altri personaggi potrebbero avere un significato speciale, ma voglio sfuggirli tutti con una barra rovesciata:
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed può fare molto di più della corrispondenza del modello di espressione regolare . Il modello di script "s / (trova) / (sostituisci) /" esegue specificamente la corrispondenza del modello.
"S / (find) / (sostituire) / (modificatori)"
- (trova) = ([^ A-Za-z0-9])
in inglese: cattura la punteggiatura o il carattere speciale come gruppo caputure 1 (\\ 1)
in inglese: prefisso tutti i caratteri speciali con una barra rovesciata
in inglese: se si trova più di una corrispondenza sulla stessa riga, sostituirli tutti
Commenta l'intero script
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.arrays.tmp.sh
Sopra usa un'espressione regolare per aggiungere il prefisso a ogni riga di ~ / .colcmp.arrays.tmp.sh con un carattere di commento bash ( # ). Lo faccio perché in seguito intendo eseguire ~ / .colcmp.arrays.tmp.sh usando il comando source e perché non conosco per certo l'intero formato di File_1.txt .
Non voglio eseguire accidentalmente codice arbitrario. Non penso che lo faccia nessuno.
"S / (find) / (sostituire) /"
in inglese: cattura ogni riga come gruppo di caputure 1 (\\ 1)
in inglese: sostituisci ogni riga con un simbolo di cancelletto seguito dalla riga che è stata sostituita
Converti valore utente in A1 [Utente] = "valore"
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
Sopra è il nucleo di questa sceneggiatura.
- converti questo:
#User1 US
- a questo:
A1[User1]="US"
- o questo:
A2[User1]="US"
(per il 2o file)
"S / (find) / (sostituire) /"
- (trova) = ^ # \\ s * (\\ S +) \\ s + (\\ S. ?) \\ s \ $
in inglese:
in inglese: sostituisci ogni riga nel formato #name value
con un operatore di assegnazione di array nel formatoA1[name]="value"
Rendi eseguibile
chmod 755 ~/.colcmp.arrays.tmp.sh
Sopra usa chmod per rendere eseguibile il file di script dell'array.
Non sono sicuro che ciò sia necessario.
Declare Associative Array (bash v4 +)
declare -A A1
La maiuscola -A indica che le variabili dichiarate saranno array associativi .
Questo è il motivo per cui lo script richiede bash v4 o superiore.
Esegui il nostro script di assegnazione delle variabili di array
source ~/.colcmp.arrays.tmp.sh
Abbiamo già:
- convertito il nostro file da righe di
User value
a righe di A1[User]="value"
,
- reso eseguibile (forse), e
- ha dichiarato A1 come un array associativo ...
Sopra abbiamo fonte lo script da eseguire nella shell corrente. Facciamo così in modo da poter mantenere i valori delle variabili che vengono impostati dallo script. Se esegui direttamente lo script, viene generata una nuova shell e i valori delle variabili vengono persi quando esce la nuova shell, o almeno questa è la mia comprensione.
Questa dovrebbe essere una funzione
cp "$2" ~/.colcmp.array2.tmp.sh
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array2.tmp.sh
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.array2.tmp.sh
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A2\\[\\1\\]=\"\\2\"/" ~/.colcmp.array2.tmp.sh
chmod 755 ~/.colcmp.array2.tmp.sh
declare -A A2
source ~/.colcmp.array2.tmp.sh
Facciamo la stessa cosa per $ 1 e A1 che facciamo per $ 2 e A2 . Dovrebbe davvero essere una funzione. Penso che a questo punto questo script sia abbastanza confuso e funzioni, quindi non lo aggiusterò.
Rileva utenti rimossi
for i in "${!A1[@]}"; do
# check for users removed
done
Sopra i cicli tramite chiavi array associative
if [ "${A2[$i]+x}" = "" ]; then
Sopra utilizza la sostituzione delle variabili per rilevare la differenza tra un valore non impostato rispetto a una variabile che è stata esplicitamente impostata su una stringa di lunghezza zero.
Apparentemente, ci sono molti modi per vedere se è stata impostata una variabile . Ho scelto quello con il maggior numero di voti.
echo "$i has changed" > Output_File
Sopra aggiunge l'utente $ i a Output_File
Rileva utenti aggiunti o modificati
USERSWHODIDNOTCHANGE=
Sopra cancella una variabile in modo da poter tenere traccia degli utenti che non sono cambiati.
for i in "${!A2[@]}"; do
# detect users added, changed and not changed
done
Sopra i cicli tramite chiavi array associative
if ! [ "${A1[$i]+x}" != "" ]; then
Sopra usa la sostituzione delle variabili per vedere se è stata impostata una variabile .
echo "$i was added as '${A2[$i]}'"
Poiché $ i è la chiave dell'array (nome utente) $ A2 [$ i] dovrebbe restituire il valore associato all'utente corrente da File_2.txt .
Ad esempio, se $ i è Utente1 , quanto sopra si legge come $ {A2 [Utente1]}
echo "$i has changed" > Output_File
Sopra aggiunge l'utente $ i a Output_File
elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
Poiché $ i è la chiave dell'array (nome utente) $ A1 [$ i] dovrebbe restituire il valore associato all'utente corrente da File_1.txt e $ A2 [$ i] dovrebbe restituire il valore da File_2.txt .
Sopra confronta i valori associati per l'utente $ i da entrambi i file.
echo "$i has changed" > Output_File
Sopra aggiunge l'utente $ i a Output_File
if [ x$USERSWHODIDNOTCHANGE != x ]; then
USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
fi
USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
Sopra crea un elenco separato da virgole di utenti che non sono cambiati. Nota che non ci sono spazi nell'elenco, altrimenti il prossimo controllo dovrebbe essere citato.
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
Sopra riporta il valore di $ USERSWHODIDNOTCHANGE ma solo se esiste un valore in $ USERSWHODIDNOTCHANGE . In questo modo, $ USERSWHODIDNOTCHANGE non può contenere spazi. Se necessita di spazi, sopra potrebbe essere riscritto come segue:
if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
diff "File_1.txt" "File_2.txt"