En gros, ce que je veux faire, c'est comparer deux fichiers par ligne et par colonne 2. Comment pourrais-je y parvenir ?
Fichier_1.txt :
User1 US
User2 US
User3 US
Fichier_2.txt :
User1 US
User2 US
User3 NG
Output_File :
User3 has changed
En gros, ce que je veux faire, c'est comparer deux fichiers par ligne et par colonne 2. Comment pourrais-je y parvenir ?
Fichier_1.txt :
User1 US
User2 US
User3 US
Fichier_2.txt :
User1 US
User2 US
User3 NG
Output_File :
User3 has changed
En s'en tenant littéralement à la question (fichier1, fichier2, fichier de sortie avec message "a changé") le script ci-dessous fonctionne.
Copiez le script dans un fichier vide, enregistrez-le sous le nom de compare.py
pour le rendre exécutable, exécutez-le par la commande :
/path/to/compare.py <file1> <file2> <outputfile>
Le script :
#!/usr/bin/env python
import sys
file1 = sys.argv[1]; file2 = sys.argv[2]; outfile = sys.argv[3]
def readfile(file):
with open(file) as compare:
return [item.replace("\n", "").split(" ") for item in compare.readlines()]
data1 = readfile(file1); data2 = readfile(file2)
mismatch = [item[0] for item in data1 if not item in data2]
with open(outfile, "wt") as out:
for line in mismatch:
out.write(line+" has changed"+"\n")
Avec quelques lignes supplémentaires, vous pouvez faire en sorte qu'il s'imprime soit dans un fichier de sortie, soit dans le terminal, selon que le fichier de sortie est défini ou non :
Pour imprimer dans un fichier :
/path/to/compare.py <file1> <file2> <outputfile>
Pour imprimer dans la fenêtre du terminal :
/path/to/compare.py <file1> <file2>
Le script :
#!/usr/bin/env python
import sys
file1 = sys.argv[1]; file2 = sys.argv[2]
try:
outfile = sys.argv[3]
except IndexError:
outfile = None
def readfile(file):
with open(file) as compare:
return [item.replace("\n", "").split(" ") for item in compare.readlines()]
data1 = readfile(file1); data2 = readfile(file2)
mismatch = [item[0] for item in data1 if not item in data2]
if outfile != None:
with open(outfile, "wt") as out:
for line in mismatch:
out.write(line+" has changed"+"\n")
else:
for line in mismatch:
print line+" has changed"
Un moyen simple est d'utiliser colordiff
qui se comporte comme diff
mais colore sa sortie. C'est très utile pour lire les diffs. En utilisant votre exemple,
$ colordiff -u File_1.txt File_2.txt
--- File_1.txt 2016-12-24 17:59:17.409490554 -0500
+++ File_2.txt 2016-12-24 18:00:06.666719659 -0500
@@ -1,3 +1,3 @@
User1 US
User2 US
-User3 US
+User3 NG
où le u
donne un diff unifié. Voici à quoi ressemble le diff colorisé :
Installer colordiff
en courant sudo apt-get install colordiff
.
Compare les paires nom/valeur dans 2 fichiers au format name value\n
. Rédige le name
à Output_file
si elle a été modifiée. Nécessite bash v4+ pour tableaux associatifs .
$ ./colcmp.sh File_1.txt File_2.txt
User3 changed from 'US' to 'NG'
no change: User1,User2
$ cat Output_File
User3 has changed
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
Décomposition du code et de sa signification, au mieux de ma compréhension. Les modifications et les suggestions sont les bienvenues.
cmp -s "$1" "$2"
case "$?" in
0)
# match
;;
1)
# compare
;;
*)
# error
;;
esac
cmp définira la valeur de $ ? comme suit :
J'ai choisi d'utiliser un cas .. esac pour évaluer l'instruction $ ? parce que la valeur de $ ? change après chaque commande, y compris test ([).
Alternativement, j'aurais pu utiliser une variable pour contenir 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 l'instruction case. Je ne sais pas ce que je préfère.
echo "" > Output_File
Ce qui précède efface le fichier de sortie de sorte que si aucun utilisateur n'a changé, le fichier de sortie sera vide.
Je fais cela à l'intérieur de la cas afin que les Fichier de sortie reste inchangé en cas d'erreur.
cp "$1" ~/.colcmp.arrays.tmp.sh
Copies ci-dessus Fichier_1.txt dans le répertoire personnel de l'utilisateur actuel.
Par exemple, si l'utilisateur actuel est john, la commande ci-dessus sera identique à cp "Fichier_1.txt" /home/john/.colcmp.arrays.tmp.sh
En gros, je suis paranoïaque. Je sais que ces caractères pourraient avoir une signification spéciale ou exécuter un programme externe lorsqu'ils sont exécutés dans un script dans le cadre d'une affectation de variable :
Ce que je je ne sais pas c'est à quel point je ne connais pas bash. Je ne sais pas quels autres caractères peuvent avoir une signification spéciale, mais je veux tous les échapper avec un backslash :
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed peut faire beaucoup plus que correspondance de motifs par expression régulière . Le modèle script "s/(find)/(replace)/" effectue spécifiquement la correspondance du motif.
"s/(trouver)/(remplacer)/(modificateurs)"
en anglais : saisir toute ponctuation ou caractère spécial comme groupe de caputure 1 ( \\1 )
en anglais : préfixer tous les caractères spéciaux par une barre oblique inverse
en anglais : si plus d'une correspondance est trouvée sur la même ligne, remplacez-les toutes.
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 fais cela parce que plus tard j'ai l'intention d'exécuter ~/.colcmp.arrays.tmp.sh en utilisant le source et parce que je ne connais pas avec certitude le format complet de la Fichier_1.txt .
Je ne veux pas exécuter accidentellement un code arbitraire. Je ne pense pas que quiconque le veuille.
"s/(find)/(replace)/"
en anglais : capturer chaque ligne comme groupe de caputure 1 ( \\1 )
en anglais : remplacer chaque ligne par un symbole de dièse suivi de la ligne qui a été remplacée
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
Ci-dessus se trouve le cœur de ce script.
#User1 US
A1[User1]="US"
A2[User1]="US"
(pour le 2ème fichier)"s/(find)/(replace)/"
en anglais :
requiert mais ignore les premiers caractères de commentaire (#)
ignore les espaces blancs de tête
capturer le premier mot comme groupe de caputure 1 ( \\1 )
nécessitent un espace (ou une tabulation, ou un espace blanc)
capturer le reste de la ligne comme groupe de capture 2
(remplacer) = A1 \\ [ \\1\\ ]=\" \\2\ "
A1[
pour commencer l'affectation du tableau dans un tableau appelé A1
]="
]
= fermer l'affectation de tableau, par exemple A1[
Utilisateur 1 ]="
US "
=
= opérateur d'affectation, par exemple variable=valeur"
= valeur de citation pour capturer les espaces ... bien que, maintenant que j'y pense, il aurait été plus facile de laisser le code ci-dessus, qui met tout en arrière, mettre également en arrière les caractères d'espace.en anglais : remplacer chaque ligne dans le format #name value
avec un opérateur d'affectation de tableau au format A1[name]="value"
chmod 755 ~/.colcmp.arrays.tmp.sh
Utilisations ci-dessus chmod pour rendre le fichier du tableau script exécutable.
Je ne suis pas sûr que ce soit nécessaire.
declare -A A1
La majuscule -A indique que les variables déclarées seront tableaux associatifs .
C'est pourquoi le script nécessite bash v4 ou plus.
source ~/.colcmp.arrays.tmp.sh
Nous l'avons déjà fait :
User value
aux lignes de A1[User]="value"
,Au-dessus, nous source le script pour l'exécuter dans le script courant. Nous faisons cela pour pouvoir conserver les valeurs des variables qui sont définies par le script. Si vous exécutez le script directement, il génère une nouvelle script, et les valeurs des variables sont perdues lorsque la nouvelle script sort, ou du moins c'est ce que je comprends.
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 と A1 que nous faisons pour $2 と A2 . Il devrait vraiment s'agir d'une fonction. Je pense qu'à ce stade, ce script est suffisamment confus et il fonctionne, donc je ne vais pas le corriger.
for i in "${!A1[@]}"; do
# check for users removed
done
Les boucles ci-dessus passent par clés de tableaux associatifs
if [ "${A2[$i]+x}" = "" ]; then
Above utilise la substitution de variable pour détecter la différence entre une valeur qui n'est pas définie et une variable qui a été explicitement définie comme 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
USERSWHODIDNOTCHANGE=
ci-dessus efface une variable pour que nous puissions garder la trace des utilisateurs qui n'ont pas changé.
for i in "${!A2[@]}"; do
# detect users added, changed and not changed
done
Les boucles ci-dessus passent par clés de tableaux associatifs
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]}'"
Parce que $i est la clé du tableau (nom de l'utilisateur) $A2[$i] doit renvoyer la valeur associée à l'utilisateur actuel à partir de Fichier_2.txt .
Par exemple, si $i est Utilisateur 1 le texte ci-dessus se lit comme suit ${A2[Utilisateur1]}
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 de l'utilisateur) $A1[$i] doit renvoyer la valeur associée à l'utilisateur actuel à partir de Fichier_1.txt et $A2[$i] devrait retourner la valeur de Fichier_2.txt .
Le tableau ci-dessus compare les valeurs associées pour l'utilisateur $i 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 séparée par des virgules des utilisateurs qui n'ont pas changé. Notez qu'il n'y a pas d'espace dans la liste, sinon la vérification suivante devrait être citée.
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
Le rapport ci-dessus indique la valeur de $USERSWHODIDNOTCHANGE mais seulement s'il existe une valeur dans $USERSWHODIDNOTCHANGE . La façon dont c'est écrit, $USERSWHODIDNOTCHANGE ne peut pas contenir d'espaces. S'il a besoin d'espaces, le texte ci-dessus pourrait être réécrit comme suit :
if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.