Zasadniczo chcę więc porównać dwa pliki wiersz po kolumnie 2. Jak to zrobić?
Plik_1.txt:
User1 US
User2 US
User3 US
Plik_2.txt:
User1 US
User2 US
User3 NG
Plik wyjściowy:
User3 has changed
Zasadniczo chcę więc porównać dwa pliki wiersz po kolumnie 2. Jak to zrobić?
Plik_1.txt:
User1 US
User2 US
User3 US
Plik_2.txt:
User1 US
User2 US
User3 NG
Plik wyjściowy:
User3 has changed
Odpowiedzi:
Sprawdź diff
polecenie. To dobre narzędzie i możesz o tym przeczytać, pisząc man diff
na terminalu.
Polecenie, które chcesz wykonać, diff File_1.txt File_2.txt
spowoduje wyświetlenie różnicy między nimi i powinno wyglądać mniej więcej tak:
Szybka uwaga na temat odczytu wyniku trzeciego polecenia: „Strzałki” ( <
i >
) odnoszą się do wartości wiersza w lewym pliku ( <
) w porównaniu do prawego pliku ( >
), przy czym lewy plik to ten, który wprowadziłeś najpierw w wierszu poleceń, w tym przypadkuFile_1.txt
Dodatkowo można zauważyć 4. Polecenie to jest diff ... | tee Output_File
to rury wyniki uzyskane diff
w tee
, który wykłada, że wyjście do pliku, tak aby można go zapisać na później, jeśli nie chcemy, aby to wszystko zobaczyć po prawej stronie konsoli tego drugiego.
diff file1 file2 -s
. Oto przykład: imgur.com/ShrQx9x
Lub możesz użyć Meld Diff
Meld pomaga porównywać pliki, katalogi i projekty z kontrolą wersji. Zapewnia dwukierunkowe i trzykrotne porównanie plików i katalogów oraz obsługuje wiele popularnych systemów kontroli wersji.
Zainstaluj, uruchamiając:
sudo apt-get install meld
Twój przykład:
Porównaj katalog:
Przykład z pełnym tekstem:
dos
a drugi w unix
.
FWIW, raczej podoba mi się to, co otrzymuję z wyjściowym wyjściem z diff
diff -y -W 120 File_1.txt File_2.txt
dałby coś takiego:
User1 US User1 US
User2 US User2 US
User3 US | User3 NG
Możesz użyć polecenia cmp
:
cmp -b "File_1.txt" "File_2.txt"
wyjście byłoby
a b differ: byte 25, line 3 is 125 U 116 N
cmp
jest znacznie szybszy niż diff
gdyby wszystko, czego chcesz, to kod powrotu.
Przyklejając się do pytania (plik1, plik2, plik wyjściowy z komunikatem „zmieniłem”) poniższy skrypt działa.
Skopiuj skrypt do pustego pliku, zapisz go jako compare.py
, wykonaj go, uruchom za pomocą polecenia:
/path/to/compare.py <file1> <file2> <outputfile>
Scenariusz:
#!/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")
Za pomocą kilku dodatkowych wierszy możesz wydrukować plik wyjściowy lub terminal, w zależności od tego, czy plik wyjściowy jest zdefiniowany:
Aby wydrukować do pliku:
/path/to/compare.py <file1> <file2> <outputfile>
Aby wydrukować do okna terminala:
/path/to/compare.py <file1> <file2>
Scenariusz:
#!/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"
Prostym sposobem jest użycie colordiff
, które zachowuje się jak, diff
ale koloruje jego wynik. Jest to bardzo pomocne przy czytaniu różnic. Korzystając z twojego przykładu,
$ 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
gdzie u
opcja daje zunifikowaną różnicę. Tak wygląda pokolorowany plik różnicowy:
Zainstaluj colordiff
, uruchamiając sudo apt-get install colordiff
.
Jeśli nie musisz wiedzieć, które części plików się różnią, możesz użyć sumy kontrolnej pliku. Można to zrobić na wiele sposobów, używając md5sum
lub sha256sum
. Zasadniczo każdy z nich wyświetla ciąg znaków, do którego hash zawartości pliku. Jeśli oba pliki są takie same, ich skrót również będzie taki sam. Jest to często używane podczas pobierania oprogramowania, takiego jak obrazy ISO instalacji Ubuntu. Są często używane do sprawdzania integralności pobranych treści.
Rozważ poniższy skrypt, w którym możesz podać dwa pliki jako argumenty, a plik powie ci, czy są takie same, czy nie.
#!/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
Przykładowy przebieg:
$ ./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
Ponadto istnieje comm
polecenie, które porównuje dwa posortowane pliki i daje wynik w 3 kolumnach: kolumna 1 dla elementów unikalnych dla pliku nr 1, kolumna 2 dla elementów unikalnych dla pliku nr 2 i kolumna 3 dla elementów obecnych w obu plikach.
Aby pominąć dowolną kolumnę, możesz użyć przełączników -1, -2 i -3. Użycie -3 pokaże linie, które się różnią.
Poniżej możesz zobaczyć zrzut ekranu polecenia w akcji.
Jest tylko jeden wymóg - pliki muszą być posortowane, aby można je było poprawnie porównać. sort
w tym celu można użyć polecenia. Poniżej znajduje się inny zrzut ekranu, w którym pliki są sortowane, a następnie porównywane. Linie zaczynające się od lewej bellong tylko do pliku_1, linie zaczynające się w kolumnie 2 należą tylko do pliku_2
Zainstaluj git i użyj
$ git diff filename1 filename2
Otrzymasz wyjście w ładnym, kolorowym formacie
Instalacja Git
$ apt-get update
$ apt-get install git-core
Porównuje pary nazwa / wartość w 2 plikach w formacie name value\n
. Zapisuje name
do, Output_file
jeśli zmieniono. Wymaga bash v4 + dla tablic asocjacyjnych .
$ ./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
Podział kodu i jego znaczenie, o ile mi wiadomo. Z zadowoleniem przyjmuję zmiany i sugestie.
cmp -s "$1" "$2"
case "$?" in
0)
# match
;;
1)
# compare
;;
*)
# error
;;
esac
cmp ustawi wartość $? w następujący sposób :
Zdecydowałem się użyć instrukcji case .. esac do oceny $? ponieważ wartość $? zmienia się po każdym poleceniu, w tym test ([).
Alternatywnie mogłem użyć zmiennej do przechowywania wartości $? :
cmp -s "$1" "$2"
CMPRESULT=$?
if [ $CMPRESULT -eq 0 ]; then
# match
elif [ $CMPRESULT -eq 1 ]; then
# compare
else
# error
fi
Powyżej robi to samo co instrukcja case. IDK, który lubię bardziej.
echo "" > Output_File
Powyżej usuwa plik wyjściowy, więc jeśli żaden użytkownik się nie zmieni, plik wyjściowy będzie pusty.
Robię to wewnątrz instrukcji case , aby plik wyjściowy pozostał niezmieniony po błędzie.
cp "$1" ~/.colcmp.arrays.tmp.sh
Powyżej kopiuje plik_1.txt do katalogu domowego bieżącego użytkownika.
Na przykład, jeśli bieżącym użytkownikiem jest John, powyższe byłoby takie samo jak cp „File_1.txt” /home/john/.colcmp.arrays.tmp.sh
Zasadniczo jestem paranoikiem. Wiem, że te znaki mogą mieć specjalne znaczenie lub wykonywać zewnętrzny program, gdy są uruchamiane w skrypcie w ramach przypisywania zmiennych:
Co ja nie wiem , jak bardzo nie wiem o bash. Nie wiem, jakie inne postacie mogą mieć specjalne znaczenie, ale chcę uciec przed nimi wszystkimi odwrotnym ukośnikiem:
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed potrafi znacznie więcej niż dopasowywanie wzorców wyrażeń regularnych . Wzorzec skryptu „s / (find) / (replace) /” konkretnie wykonuje dopasowanie wzorca.
„s / (znajdź) / (zamień) / (modyfikatory)”
w języku angielskim: przechwytuj dowolne znaki interpunkcyjne lub specjalne jako grupę kaputur 1 (\\ 1)
w języku angielskim: poprzedź wszystkie znaki specjalne odwrotnym ukośnikiem
w języku angielskim: jeśli w tym samym wierszu znaleziono więcej niż jedno dopasowanie, zamień je wszystkie
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.arrays.tmp.sh
Powyżej używa wyrażenia regularnego, aby poprzedzić każdą linię ~ / .colcmp.arrays.tmp.sh znakiem komentarza bash ( # ). Robię to, ponieważ później zamierzam wykonać ~ / .colcmp.arrays.tmp.sh za pomocą polecenia source i ponieważ nie wiem na pewno cały format pliku_1.txt .
Nie chcę przypadkowo wykonać dowolnego kodu. Nie sądzę, żeby ktokolwiek to zrobił.
„s / (znajdź) / (zamień) /”
w języku angielskim: przechwytuj każdą linię jako grupę caputure 1 (\\ 1)
w języku angielskim: zamień każdą linię na symbol funta, a następnie linię, która została zastąpiona
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
Powyżej znajduje się rdzeń tego skryptu.
#User1 US
A1[User1]="US"
A2[User1]="US"
(dla drugiego pliku)„s / (znajdź) / (zamień) /”
po angielsku:
przechwyć resztę linii jako grupę przechwytywania 2
(zamień) = A1 \\ [\\ 1 \\] = \ "\\ 2 \"
A1[
aby rozpocząć przypisanie tablicy w tablicy o nazwieA1
]="
]
= bliskie przypisanie tablicy, np. A1[
Użytkownik1 ]="
USA"
=
= operator przypisania, np. zmienna = wartość"
= podaj wartość, aby uchwycić spacje ... chociaż teraz, gdy o tym myślę, łatwiej byłoby pozwolić kodowi powyżej, który odwraca wszystko, także do znaków odwrotnego ukośnika.w języku angielskim: zastąp każdą linię w formacie #name value
operatorem przypisania tablicy w formacieA1[name]="value"
chmod 755 ~/.colcmp.arrays.tmp.sh
Powyżej używa chmod, aby plik skryptu tablicowego był wykonywalny.
Nie jestem pewien, czy jest to konieczne.
declare -A A1
Wielka-A oznacza, że zadeklarowane zmienne będą tablicami asocjacyjnymi .
Dlatego skrypt wymaga bash v4 lub nowszego.
source ~/.colcmp.arrays.tmp.sh
Mamy już:
User value
na linie A1[User]="value"
,Powyżej my źródła skryptu, aby uruchomić go w bieżącej powłoki. Robimy to, abyśmy mogli zachować wartości zmiennych ustawiane przez skrypt. Jeśli skrypt zostanie wykonany bezpośrednio, odradza się nowa powłoka, a wartości zmiennych są tracone po wyjściu z nowej powłoki, a przynajmniej tak rozumiem.
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
Robimy to samo za 1 USD i A1 , co za 2 USD i A2 . To naprawdę powinna być funkcja. Myślę, że w tym momencie ten skrypt jest dość mylący i działa, więc go nie naprawię.
for i in "${!A1[@]}"; do
# check for users removed
done
Powyższe pętle za pomocą kluczy tablicy asocjacyjnej
if [ "${A2[$i]+x}" = "" ]; then
Powyżej używa podstawienia zmiennej w celu wykrycia różnicy między wartością, która nie jest ustawiona, a zmienną, która została jawnie ustawiona na ciąg o zerowej długości.
Najwyraźniej istnieje wiele sposobów sprawdzenia, czy zmienna została ustawiona . Wybrałem ten, który ma najwięcej głosów.
echo "$i has changed" > Output_File
Powyżej dodaje użytkownika $ i do pliku_wyjściowego
USERSWHODIDNOTCHANGE=
Powyżej usuwa zmienną, dzięki czemu możemy śledzić użytkowników, którzy się nie zmienili.
for i in "${!A2[@]}"; do
# detect users added, changed and not changed
done
Powyższe pętle za pomocą kluczy tablicy asocjacyjnej
if ! [ "${A1[$i]+x}" != "" ]; then
Powyżej używa podstawienia zmiennej, aby sprawdzić, czy zmienna została ustawiona .
echo "$i was added as '${A2[$i]}'"
Ponieważ $ i jest kluczem tablicy (nazwa użytkownika) $ A2 [$ i] powinien zwrócić wartość powiązaną z bieżącym użytkownikiem z pliku_2.txt .
Na przykład jeśli $ i to Użytkownik1 , powyższy tekst brzmi jako $ {A2 [Użytkownik1]}
echo "$i has changed" > Output_File
Powyżej dodaje użytkownika $ i do pliku_wyjściowego
elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
Ponieważ $ i jest kluczem tablicy (nazwa użytkownika) $ A1 [$ i] powinien zwrócić wartość powiązaną z bieżącym użytkownikiem z pliku_1.txt , a $ A2 [$ i] powinien zwrócić wartość z pliku_2.txt .
Powyżej porównuje powiązane wartości dla użytkownika $ i z obu plików.
echo "$i has changed" > Output_File
Powyżej dodaje użytkownika $ i do pliku_wyjściowego
if [ x$USERSWHODIDNOTCHANGE != x ]; then
USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
fi
USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
Powyżej tworzy oddzieloną przecinkami listę użytkowników, którzy się nie zmienili. Uwaga: na liście nie ma spacji, w przeciwnym razie należałoby zacytować następny czek.
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
Powyżej zgłasza wartość $ USERSWHODIDNOTCHANGE, ale tylko jeśli wartość jest w US $ USERSWHODIDNOTCHANGE . Sposób zapisu: $ USERSWHODIDNOTCHANGE nie może zawierać spacji. Jeśli potrzebuje spacji, powyższe można przepisać w następujący sposób:
if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
diff "File_1.txt" "File_2.txt"