colcmp.sh
Compare les paires nom / valeur dans 2 fichiers au format name value\n
. Écrit le name
à Output_file
si changé. Nécessite bash v4 + pour les tableaux associatifs .
Usage
$ ./colcmp.sh File_1.txt File_2.txt
User3 changed from 'US' to 'NG'
no change: User1,User2
Fichier de sortie
$ cat Output_File
User3 has changed
Source (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
Explication
Décomposition du code et de sa signification, au mieux de ma compréhension. Je me félicite des modifications et des suggestions.
Comparaison de fichier de base
cmp -s "$1" "$2"
case "$?" in
0)
# match
;;
1)
# compare
;;
*)
# error
;;
esac
cmp définira la valeur de $? comme suit :
- 0 = fichiers correspondants
- 1 = les fichiers diffèrent
- 2 = erreur
J'ai choisi d'utiliser une instruction case .. esac to evalute $? parce que la valeur de $? change après chaque commande, y compris test ([).
Sinon, j'aurais pu utiliser une variable pour conserver la valeur de $? :
cmp -s "$1" "$2"
CMPRESULT=$?
if [ $CMPRESULT -eq 0 ]; then
# match
elif [ $CMPRESULT -eq 1 ]; then
# compare
else
# error
fi
Ci-dessus fait la même chose que la déclaration de cas. IDK que j'aime mieux.
Effacer la sortie
echo "" > Output_File
L'option ci-dessus efface le fichier de sortie. Par conséquent, si aucun utilisateur ne change, le fichier de sortie sera vide.
Je le fais dans les instructions case afin que le fichier de sortie reste inchangé en cas d'erreur.
Copier le fichier utilisateur dans un script shell
cp "$1" ~/.colcmp.arrays.tmp.sh
Ci-dessus copie File_1.txt dans le répertoire de départ de l'utilisateur actuel.
Par exemple, si l'utilisateur actuel est john, la procédure ci-dessus serait identique à cp "File_1.txt" /home/john/.colcmp.arrays.tmp.sh.
Échapper à des caractères spéciaux
En gros, je suis paranoïaque. Je sais que ces caractères peuvent avoir une signification particulière ou exécuter un programme externe lorsqu'ils sont exécutés dans un script dans le cadre d'une affectation de variable:
- `- back-tick - exécute un programme et la sortie comme si la sortie faisait partie de votre script
- $ - dollar sign - préfixe généralement une variable
- $ {} - permet une substitution de variable plus complexe
- $ () - idk ce que cela fait mais je pense qu'il peut exécuter du code
Ce que je ne sais pas, c'est combien je ne sais pas à propos de bash. Je ne sais pas quels autres personnages pourraient avoir une signification particulière, mais je veux leur échapper avec une barre oblique inverse:
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed peut faire beaucoup plus que la correspondance de motif d'expression régulière . Le modèle de script "s / (trouver) / (remplacer) /" effectue spécifiquement la correspondance de modèle.
"s / (trouver) / (remplacer) / (modificateurs)"
- (trouver) = ([^ A-Za-z0-9])
en anglais: saisir toute ponctuation ou caractère spécial en tant que groupe de caputures 1 (\\ 1)
- (remplacez) = \\\\\\ 1
- \\\\ = caractère littéral (\\) c'est-à-dire une barre oblique inverse
- \\ 1 = groupe de capture 1
en anglais: préfixe tous les caractères spéciaux avec une barre oblique inverse
- (modificateurs) = g
- g = remplacer globalement
en anglais: si plus d'une correspondance est trouvée sur la même ligne, remplacez-les toutes
Commentez tout le script
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.arrays.tmp.sh
Ci-dessus utilise une expression régulière pour préfixer chaque ligne de ~ / .colcmp.arrays.tmp.sh avec un caractère de commentaire bash ( # ). Je le fais parce que plus tard, j'ai l'intention d'exécuter ~ / .colcmp.arrays.tmp.sh en utilisant la commande source et parce que je ne sais pas avec certitude le format entier de File_1.txt .
Je ne veux pas exécuter accidentellement du code arbitraire. Je pense que personne ne le fait.
"s / (trouver) / (remplacer) /"
en anglais: capturez chaque ligne en tant que groupe de capacités 1 (\\ 1)
- (remplacez) = # \\ 1
- # = caractère littéral (#), c.-à-d. un symbole dièse ou hachage
- \\ 1 = groupe de capture 1
en anglais: remplacez chaque ligne par un symbole dièse suivi de la ligne remplacée
Convertir une valeur utilisateur en A1 [User] = "valeur"
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
Ci-dessus se trouve le noyau de ce script.
- Convertir cela:
#User1 US
- pour ça:
A1[User1]="US"
- ou ceci:
A2[User1]="US"
(pour le 2ème fichier)
"s / (trouver) / (remplacer) /"
- (trouver) = ^ # \\ s * (\\ S +) \\ s + (\\ S. ?) \\ s \ $
en anglais:
en anglais: remplacez chaque ligne du format #name value
par un opérateur d'affectation de tableau au formatA1[name]="value"
Rendre exécutable
chmod 755 ~/.colcmp.arrays.tmp.sh
Ci-dessus utilise chmod pour rendre le fichier de script de tableau exécutable.
Je ne sais pas si c'est nécessaire.
Déclarer un tableau associatif (bash v4 +)
declare -A A1
Le capital -A indique que les variables déclarées seront des tableaux associatifs .
C'est pourquoi le script nécessite bash version 4 ou supérieure.
Exécuter notre script d'assignation de variable de tableau
source ~/.colcmp.arrays.tmp.sh
Nous avons déjà:
- converti notre fichier de lignes
User value
en lignes de A1[User]="value"
,
- l'a rendu exécutable (peut-être), et
- a déclaré A1 comme un tableau associatif ...
Au- dessus de nous la source du script pour l'exécuter dans le shell courant. Nous faisons cela afin de pouvoir conserver les valeurs de variables définies par le script. Si vous exécutez le script directement, il génère un nouveau shell et les valeurs des variables sont perdues lorsque le nouveau shell se ferme, ou du moins c'est ce que je comprends.
Cela devrait être une fonction
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
Nous faisons la même chose pour 1 $ et A1 que pour 2 $ et A2 . Cela devrait vraiment être une fonction. Je pense qu'à ce stade, ce script est assez déroutant et qu'il fonctionne, donc je ne vais pas le réparer.
Détecter les utilisateurs supprimés
for i in "${!A1[@]}"; do
# check for users removed
done
Boucles ci-dessus à travers des clés de tableau associatif
if [ "${A2[$i]+x}" = "" ]; then
La méthode décrite ci-dessus utilise la substitution de variable pour détecter la différence entre une valeur non définie et une variable explicitement définie sur une chaîne de longueur nulle.
Apparemment, il y a beaucoup de façons de voir si une variable a été définie . J'ai choisi celui qui a reçu le plus de votes.
echo "$i has changed" > Output_File
Ci-dessus ajoute l'utilisateur $ i au fichier de sortie
Détecter les utilisateurs ajoutés ou modifiés
USERSWHODIDNOTCHANGE=
Ci-dessus efface une variable pour que nous puissions garder une trace des utilisateurs qui n'ont pas changé.
for i in "${!A2[@]}"; do
# detect users added, changed and not changed
done
Boucles ci-dessus à travers des clés de tableau associatif
if ! [ "${A1[$i]+x}" != "" ]; then
Ci-dessus utilise la substitution de variable pour voir si une variable a été définie .
echo "$i was added as '${A2[$i]}'"
Comme $ i est la clé du tableau (nom d'utilisateur), $ A2 [$ i] doit renvoyer la valeur associée à l'utilisateur actuel à partir de File_2.txt .
Par exemple, si $ i est Utilisateur1 , ce qui précède se lit comme suit : {{A2 [User1]}]
echo "$i has changed" > Output_File
Ci-dessus ajoute l'utilisateur $ i au fichier de sortie
elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
Parce que $ i est la clé du tableau (nom d'utilisateur), $ A1 [$ i] doit renvoyer la valeur associée à l'utilisateur actuel à partir de File_1.txt et $ A2 [$ i] doit renvoyer la valeur à partir de File_2.txt .
Ci-dessus compare les valeurs associées pour l'utilisateur $ i à partir des deux fichiers.
echo "$i has changed" > Output_File
Ci-dessus ajoute l'utilisateur $ i au fichier de sortie
if [ x$USERSWHODIDNOTCHANGE != x ]; then
USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
fi
USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
Ci-dessus crée une liste d'utilisateurs séparés par des virgules qui n'ont pas changé. Notez qu'il n'y a pas d'espaces dans la liste, sinon la coche suivante devra être citée.
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
Ci-dessus indique la valeur de $ USERSWHODIDNOTCHANGE, mais uniquement s'il existe une valeur dans $ USERSWHODIDNOTCHANGE . De la manière dont cela est écrit, $ USERSWHODIDNOTCHANGE ne peut contenir aucun espace. S'il y a un besoin d'espaces, les éléments ci-dessus pourraient être réécrits comme suit:
if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
diff "File_1.txt" "File_2.txt"