Quindi, fondamentalmente, ciò che voglio fare è confrontare due file riga per colonna 2. Come posso ottenere questo risultato?
File_1.txt:
User1 US
User2 US
User3 US
File_2.txt:
User1 US
User2 US
User3 NG
File di uscita:
User3 has changed
Quindi, fondamentalmente, ciò che voglio fare è confrontare due file riga per colonna 2. Come posso ottenere questo risultato?
File_1.txt:
User1 US
User2 US
User3 US
File_2.txt:
User1 US
User2 US
User3 NG
File di uscita:
User3 has changed
Risposte:
Guarda il diffcomando. È un buon strumento e puoi leggere tutto scrivendo man diffnel tuo terminale.
Il comando che vorrai fare è diff File_1.txt File_2.txtche produrrà la differenza tra i due e dovrebbe assomigliare a questo:

Una breve nota sulla lettura dell'output dal terzo comando: Le 'frecce' ( <e >) si riferiscono a quale sia il valore della riga nel file di sinistra ( <) rispetto al file di destra ( >), con il file di sinistra che è quello che hai inserito prima nella riga di comando, in questo casoFile_1.txt
Inoltre, potresti notare che il quarto comando è che diff ... | tee Output_Fileinoltra i risultati diffin a tee, che quindi inserisce l'output in un file, in modo da poterlo salvare in un secondo momento se non vuoi vederlo tutto sulla console proprio in quel secondo.
diff file1 file2 -s. Ecco un esempio: imgur.com/ShrQx9x
Oppure puoi usare Meld Diff
Combinazione ti aiuta a confrontare file, directory e progetti controllati dalla versione. Fornisce un confronto a due e tre vie di file e directory e ha il supporto per molti sistemi di controllo di versione popolari.
Installa eseguendo:
sudo apt-get install meld
Il tuo esempio:

Confronta directory:

Esempio con pieno di testo:

dose il secondo in unix.
FWIW, preferisco quello che ottengo con l'output side-by-side di diff
diff -y -W 120 File_1.txt File_2.txt
darebbe qualcosa del tipo:
User1 US User1 US
User2 US User2 US
User3 US | User3 NG
Puoi usare il comando cmp:
cmp -b "File_1.txt" "File_2.txt"
l'uscita sarebbe
a b differ: byte 25, line 3 is 125 U 116 N
cmpè molto più veloce che diffse tutto ciò che desideri è il codice di ritorno.
Litteraly aderisce alla domanda (file1, file2, outputfile con il messaggio "è cambiato") lo script seguente funziona.
Copia lo script in un file vuoto, salvalo come compare.py, rendilo eseguibile, eseguilo con il comando:
/path/to/compare.py <file1> <file2> <outputfile>
Il copione:
#!/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")
Con alcune righe extra, puoi farlo stampare su un file di output o sul terminale, a seconda che sia definito il file di output:
Per stampare su un file:
/path/to/compare.py <file1> <file2> <outputfile>
Per stampare sulla finestra del terminale:
/path/to/compare.py <file1> <file2>
Il copione:
#!/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 modo semplice è usare colordiff, che si comporta come diffma colora il suo output. Questo è molto utile per leggere le differenze. Usando il tuo esempio,
$ 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
dove l' uopzione fornisce un diff unificato. Ecco come appare il diff colorato:
Installa colordiffeseguendo sudo apt-get install colordiff.
Se non è necessario sapere quali parti dei file differiscono, è possibile utilizzare il checksum del file. Ci sono molti modi per farlo, usando md5sumo sha256sum. Fondamentalmente, ognuno di essi genera una stringa in cui l'hash del contenuto di un file. Se i due file sono uguali, anche il loro hash sarà lo stesso. Questo è spesso usato quando si scaricano software, come immagini iso di installazione di Ubuntu. Vengono spesso utilizzati per verificare l'integrità di un contenuto scaricato.
Considera lo script di seguito, in cui puoi fornire due file come argomenti e il file ti dirà se sono uguali o meno.
#!/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
Esecuzione di esempio:
$ ./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
Inoltre c'è il commcomando, che confronta due file ordinati, e fornisce l'output in 3 colonne: colonna 1 per elementi univoci al file n. 1, colonna 2 per elementi univoci al file n. 2 e colonna 3 per gli elementi presenti in entrambi i file.
Per sopprimere una delle colonne è possibile utilizzare gli switch -1, -2 e -3. L'uso di -3 mostrerà le linee che differiscono.
Di seguito puoi vedere lo screenshot del comando in azione.

C'è solo un requisito: i file devono essere ordinati per poter essere confrontati correttamente. sortcomando può essere utilizzato a tale scopo. Bellow è un altro screenshot, in cui i file vengono ordinati e confrontati. Le righe che iniziano sulla sinistra a solo File_1, le righe che iniziano sulla colonna 2 appartengono solo a File_2

Installa git e usa
$ git diff filename1 filename2
E otterrai un output in un bel formato colorato
Installazione Git
$ apt-get update
$ apt-get install git-core
Confronta le coppie nome / valore in 2 file nel formato name value\n. Scrive namea Output_filese modificato. Richiede bash v4 + per array associativi .
$ ./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
Analisi del codice e del suo significato, per quanto ne so. Accolgo con favore modifiche e suggerimenti.
cmp -s "$1" "$2"
case "$?" in
0)
# match
;;
1)
# compare
;;
*)
# error
;;
esac
cmp imposterà il valore di $? come segue :
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ù.
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.
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
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:
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)"
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
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
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
Sopra è il nucleo di questa sceneggiatura.
#User1 US
A1[User1]="US"A2[User1]="US"(per il 2o file)"S / (find) / (sostituire) /"
in inglese:
acquisire il resto della linea come gruppo di acquisizione 2
(sostituisci) = A1 \\ [\\ 1 \\] = \ "\\ 2 \"
A1[per iniziare l'assegnazione di array in un array chiamatoA1]="
]= assegnazione di array vicini, ad es. A1[Utente1 ]="USA"= = operatore di assegnazione, ad es. variabile = valore" = valore di citazione per catturare spazi ... anche se ora che ci penso, sarebbe stato più facile lasciare che il codice sopra che rovesciasse tutto per rovesciare anche i caratteri dello spazio.in inglese: sostituisci ogni riga nel formato #name valuecon un operatore di assegnazione di array nel formatoA1[name]="value"
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 -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.
source ~/.colcmp.arrays.tmp.sh
Abbiamo già:
User valuea righe di A1[User]="value",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.
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ò.
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
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"