Donc, en gros, ce que je veux faire, c'est comparer deux fichiers ligne par colonne 2. Comment pourrais-je accomplir cela?
Fichier_1.txt:
User1 US
User2 US
User3 US
Fichier_2.txt:
User1 US
User2 US
User3 NG
Fichier de sortie:
User3 has changed
Examinez la commande diff
. C'est un bon outil, et vous pouvez tout lire à ce sujet en tapant man diff
dans votre terminal.
La commande que vous voulez faire est diff File_1.txt File_2.txt
qui affichera la différence entre les deux et devrait ressembler à ceci:
Une note rapide sur la lecture du résultat de la troisième commande: Les "flèches" (<
et >
) font référence à la valeur de la ligne dans le fichier de gauche (<
) par rapport au fichier de droite (>
), le fichier de gauche étant celui que vous avez entré en premier sur la ligne de commande, dans ce cas, File_1.txt
De plus, vous remarquerez peut-être que la 4ème commande est diff ... | tee Output_File
, ce qui dirige les résultats de diff
dans un tee
, qui place ensuite cette sortie dans un fichier, afin que vous puissiez l'enregistrer pour plus tard si vous ne voulez pas tout afficher à droite. cette seconde.
Ou vous pouvez utiliser Meld Diff
Meld vous aide à comparer des fichiers, des répertoires et des projets à version contrôlée. Il fournit une comparaison à deux et à trois voies des fichiers et des répertoires et prend en charge de nombreux systèmes de contrôle de version populaires.
Installez en exécutant:
Sudo apt-get install meld
Votre exemple:
Comparer le répertoire:
Exemple avec plein de texte:
FWIW, j'aime bien ce que j'ai avec la sortie côte à côte de diff
diff -y -W 120 File_1.txt File_2.txt
donnerait quelque chose comme:
User1 US User1 US
User2 US User2 US
User3 US | User3 NG
Meld
est un très bon outil. Mais vous pouvez également utiliser diffuse
pour comparer visuellement deux fichiers:
diffuse file1.txt file2.txt
Vous pouvez utiliser la commande cmp
NAME _ :
cmp -b "File_1.txt" "File_2.txt"
la sortie serait
a b differ: byte 25, line 3 is 125 U 116 N
Si vous vous en tenez à la question (fichier1, fichier2, fichier de sortie avec le message "a changé"), le script ci-dessous fonctionne.
Copiez le script dans un fichier vide, enregistrez-le sous compare.py
, rendez-le exécutable, exécutez-le à l'aide de la commande suivante:
/path/to/compare.py <file1> <file2> <outputfile>
Le scénario:
#!/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 le faire soit imprimer dans un fichier de sortie, soit dans le terminal, selon que le fichier de sortie est défini:
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 scénario:
#!/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 consiste à utiliser colordiff
name__, qui se comporte comme diff
mais colorise sa sortie. Ceci 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ù l'option u
donne un diff unifié. Voici à quoi ressemble le diff colorisé:
Installez colordiff
en exécutant Sudo apt-get install colordiff
.
Installer git et utiliser
$ git diff filename1 filename2
Et vous obtiendrez une sortie au format Nice coloré
Git installation
$ apt-get update
$ apt-get install git-core
S'il n'est pas nécessaire de savoir quelles parties des fichiers diffèrent, vous pouvez utiliser la somme de contrôle du fichier. Il y a plusieurs façons de le faire, en utilisant md5sum
ou sha256sum
. Fondamentalement, chacun d’eux génère une chaîne contenant le hachage du fichier. Si les deux fichiers sont identiques, leur hachage sera également identique. Ceci est souvent utilisé lorsque vous téléchargez un logiciel, tel que des images iso d'installation Ubuntu. Ils sont souvent utilisés pour vérifier l'intégrité d'un contenu téléchargé.
Considérons le script ci-dessous, où vous pouvez donner deux fichiers en arguments, et le fichier vous dira s’ils sont identiques ou non.
#!/bin/bash
# Check if both files exist
if ! [ -e "$1" ];
then
printf "%s doesn't exist\n" "$1"
exit 2
Elif ! [ -e "$2" ]
then
printf "%s doesn't exist\n" "$2"
exit 2
fi
# Get checksums of eithe file
file1_sha=$( sha256sum "$1" | awk '{print $1}')
file2_sha=$( sha256sum "$2" | awk '{print $1}')
# Compare the checksums
if [ "x$file1_sha" = "x$file2_sha" ]
then
printf "Files %s and %s are the same\n" "$1" "$2"
exit 0
else
printf "Files %s and %s are different\n" "$1" "$2"
exit 1
fi
Échantillon échantillon:
$ ./compare_files.sh /etc/passwd ./passwd_copy.txt
Files /etc/passwd and ./passwd_copy.txt are the same
$ echo $?
0
$ ./compare_files.sh /etc/passwd /etc/default/grub
Files /etc/passwd and /etc/default/grub are different
$ echo $?
1
De plus, la commande comm
compare deux fichiers triés et génère une sortie en 3 colonnes: la colonne 1 pour les éléments uniques du fichier n ° 1, la colonne 2 pour les éléments uniques du fichier n ° 2 et la colonne 3 pour les éléments présents dans les deux fichiers.
Pour supprimer l'une ou l'autre colonne, vous pouvez utiliser les commutateurs -1, -2 et -3. Utiliser -3 montrera les lignes qui diffèrent.
Ci-dessous, vous pouvez voir la capture d'écran de la commande en action.
Une seule condition est requise: les fichiers doivent être triés pour pouvoir être comparés correctement. La commande sort
peut être utilisée à cette fin. Ci-dessous, une autre capture d'écran, où les fichiers sont triés puis comparés. Les lignes commençant à gauche près de File_1 seulement, les lignes commençant à la colonne 2 appartiennent à File_2 uniquement
Compare les paires nom/valeur dans 2 fichiers au format name value\n
. Écrit le name
à Output_file
s’il a été modifié. 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. Je me félicite des modifications et des suggestions.
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 une case .. esac == instruction à évaluer $ ? parce que la valeur de $? == change après chaque commande, y compris test ) = = ([).
Sinon, 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 la déclaration de cas. IDK que j'aime mieux.
echo "" > Output_File
L'option ci-dessus efface le fichier de sortie. Ainsi, si aucun utilisateur ne change, le fichier de sortie sera vide.
Je le fais dans les instructions case afin que le Output_file reste inchangé en cas d'erreur.
cp "$1" ~/.colcmp.arrays.tmp.sh
Copies ci-dessus File_1.txt dans le répertoire personnel 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
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:
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 correspondance du modèle 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)"
en anglais: capturez toute ponctuation ou caractère spécial en tant que groupe de fonctions 1 (\\ 1)
en anglais: préfixe tous les caractères spéciaux avec une barre oblique inverse
en anglais: si plus d'une correspondance est trouvée sur la même ligne, remplacez-les tous
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 je compte exécuter ~/.colcmp.arrays.tmp.sh en utilisant la commande source et parce que je 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: capturer chaque ligne en tant que groupe de fonctions 1 (\\ 1)
en anglais: remplace chaque ligne par un symbole dièse suivi de la ligne remplacée
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
Ci-dessus est le noyau de ce script.
#User1 US
A1[User1]="US"
A2[User1]="US"
(pour le 2e fichier)"s/(trouver)/(remplacer) /"
en anglais:
capturer le reste de la ligne en tant que groupe de capture 2
(remplacez) = A1 \\ [\\ 1 \\] =\"\\ 2 \"
A1[
pour commencer l’affectation de tableau dans un tableau appelé A1
]="
]
= fermer l'assignation de tableau, par exemple. A1[
User1]="
US"
=
= opérateur d'assignation, par exemple variable = valeur"
= valeur quote pour capturer des espaces ... bien que, maintenant que j'y réfléchisse, il aurait été plus facile de laisser le code ci-dessus, qui supprime toutes les barres obliques inverses, ainsi que les espaces. en anglais: remplacez chaque ligne au format #name value
par un opérateur d'assignation de tableau au format A1[name]="value"
chmod 755 ~/.colcmp.arrays.tmp.sh
Ci-dessus, utilisez chmod pour rendre le fichier de script de tableau exécutable.
Je ne sais pas si c'est nécessaire.
declare -A A1
Le capital -A indique que les variables déclarées seront tableaux associatifs .
C'est pourquoi le script nécessite bash version 4 ou supérieure.
source ~/.colcmp.arrays.tmp.sh
Nous avons déjà:
User value
en lignes de A1[User]="value"
,Ci-dessus nous source ) == le script pour l'exécuter dans le shell actuel. Nous faisons cela afin de pouvoir conserver les valeurs de variables définies par le script. Si vous exécutez le script directement, il crée 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.
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 nous faisons 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.
for i in "${!A1[@]}"; do
# check for users removed
done
Boucles ci-dessus à travers 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 Output_File
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 clés de tableau associatif
if ! [ "${A1[$i]+x}" != "" ]; then
Ci-dessus utilise la substitution de variable à voir si une variable a été définie .
echo "$i was added as '${A2[$i]}'"
Parce que $ 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 , la valeur ci-dessus est la suivante: $ {A2 [Utilisateur1]}
echo "$i has changed" > Output_File
Ci-dessus ajoute l’utilisateur $ i au Output_File
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] devrait renvoyer la valeur de File_2.txt .
Ci-dessus compare les valeurs associées à l'utilisateur $ i à partir des deux fichiers.
echo "$i has changed" > Output_File
Ci-dessus ajoute l’utilisateur $ i au Output_File
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 $ USERSWHODIDNOTCHANGE mais uniquement s'il existe une valeur $ USERSWHODIDNOTCHANGE . La manière dont ceci est écrit, $ USERSWHODIDNOTCHANGE ne peut contenir aucun espace. S'il y a un besoin d'espaces, les éléments ci-dessus pourraient être récrit comme suit
if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi