Jak różnicować dwa pliki tekstowe w programie Windows Powershell?

96

Mam dwa pliki tekstowe i chcę znaleźć różnice między nimi za pomocą programu Windows Powershell. Czy jest dostępne coś podobnego do narzędzia Unix diff? Czy jest inny sposób, którego nie rozważałem?

Próbowałem porównać obiekt, ale otrzymałem ten tajemniczy wynik:

PS C:\> compare-object one.txt two.txt

InputObject                                                 SideIndicator
-----------                                                 -------------
two.txt                                                     =>
one.txt                                                     <=
Brian Willis
źródło

Odpowiedzi:

101

Sam to rozgryzłem. Ponieważ Powershell współpracuje raczej z obiektami .net niż z tekstem, musisz użyć get-content, aby wyświetlić zawartość plików tekstowych. Aby wykonać to, co próbowałem zrobić w pytaniu, użyj:

compare-object (get-content one.txt) (get-content two.txt)
Brian Willis
źródło
1
Byłem bardzo zaskoczony, gdy próbowałem porównać dwa pliki: nieposortowaną tablicę liczb i tę samą tablicę liczb po ich posortowaniu. Brak danych wyjściowych pomimo bardzo różnych plików. Najwyraźniej obiekt porównujący nie bierze pod uwagę kolejności.
cgmb
1
@cgmb - Myślę, że możesz -SyncWindow 0to naprawić, ale nie jestem pewien, czy zostało to niedawno wprowadzone. Nie jest to jednak szczególnie mądre.
James Ruskin
32

Prostszym sposobem na to jest napisanie:

diff (cat file1) (cat file2)
Alex Y.
źródło
15
Zróżnicowane i cat to tylko aliasy dla opcji Porównaj obiekt i Pobierz zawartość w PowerShell. To jest to samo.
Shawn Melton,
4
pomimo tego, że jest taka sama jak zaakceptowana odpowiedź, bardziej lubię używać tej składni
Elijah W. Gagne
Zauważ, że wcale nie zachowuje się jak * nix diff, jak zauważają inne odpowiedzi tutaj. A kiedy użyłem bardziej złożonego wyrażenia zamiast catotrzymałem niepoprawne dane wyjściowe, więc dołączę się do pozostałych w zaleceniu, aby uniknąć robienia tego w PowerShell, jeśli pochodzisz z * nix.
Nickolay
29

Lub możesz użyć fcpolecenia DOS w ten sposób (pokazuje to wynik obu plików, więc będziesz musiał skanować w poszukiwaniu różnic):

fc.exe filea.txt fileb.txt > diff.txt

fcjest aliasem polecenia cmdlet Format-Custom, dlatego należy wprowadzić polecenie asfc.exe . Należy pamiętać, że wiele narzędzi DOS nie obsługuje kodowania UTF-8.

Możesz także odrodzić proces CMD i uruchomić fcgo.

start cmd "/c  ""fc filea.txt fileb.txt >diff.txt"""

To instruuje PowerShell, aby rozpocząć proces z programem „cmd” przy użyciu parametrów w cudzysłowach. W cudzysłowach znajduje się opcja cmd „/ c”, która uruchamia polecenie i kończy działanie. Właściwe polecenie uruchamiane przez cmd w tym procesie fc filea.txt fileb.txtprzekierowuje dane wyjściowe do pliku diff.txt.

Możesz używać DOS fc.exez poziomu PowerShell.

phord350
źródło
2
+1 za wydobycie DOS ^ _ ^
Jeff Bridgman
1
„fc” nie działało dla mnie i nie zdawałem sobie sprawy, że muszę go określić jako „fc.exe”, aby odróżnić go od Format-Custom. Dokładnie tego szukałem. Dzięki.
Xonatron
Może jestem kompletnym filistynem, ale wydaje mi się to o wiele bardziej przydatne. Bardzo ładnie rozwiązało mój problem.
AJ.
Jedynym problemem jest to, że NIENAWIDZI Unicode.
iCodeSometime
7

diff na * nix nie jest częścią powłoki, ale osobną aplikacją.

Czy jest jakiś powód, dla którego nie można po prostu użyć pliku diff.exe w programie PowerShell?

Możesz pobrać wersję z pakietu UnxUtils ( http://unxutils.sourceforge.net/ )

Mikeage
źródło
10
Ponieważ PowerShell jest teraz dołączony, nie ma potrzeby pobierania i instalowania.
Bratch
Właśnie skończyłem używać git diff, ponieważ już go zainstalowałem. Ani fc.exenie Compare-Objectwyprodukowałem oczekiwanych wyników.
Raziel
4

porównaj-obiekt (alias diff) jest żałosny, jeśli spodziewasz się, że będzie on zachowywał się jak diff uniksowy. Wypróbowałem diff (plik gc1) (plik gc2), a jeśli linia jest za długa, nie widzę rzeczywistego pliku różnicowego i, co ważniejsze, nie mogę powiedzieć, na którym numerze linii znajduje się plik różnicowy.

Kiedy próbuję dodać -passthru, teraz widzę różnicę, ale tracę plik, w którym jest różnica, i nadal nie otrzymuję numeru linii.

Moja rada, nie używaj programu PowerShell do znajdowania różnic w plikach. Jak ktoś zauważył, fc działa i działa trochę lepiej niż porównywać obiekty, a jeszcze lepiej jest pobieranie i używanie prawdziwych narzędzi, takich jak emulator unixowy, o którym wspomniał Mikeage.

Marc Towersap
źródło
Wydaje się również, że dokonuje porównania zestawu (tj. -SyncWindowIgnoruje kolejność), ponieważ domyślnie jest to maksymalne. Ustawienie tej wartości na 0 nie powoduje, że działa tak samo jak diff... A kiedy przekazałem potok (... | select-object ...)jako dane wejściowe, po prostu wydrukowałem bzdury, więc się poddałem.
Nickolay
3

Jak zauważyli inni, jeśli spodziewałeś się wyjścia diff unix-y, użycie aliasu diff PowerShell mocno cię zawiodło. Po pierwsze, musisz trzymać rękę przy czytaniu plików (z gc / get-content). Po drugie, wskaźnik różnicy znajduje się po prawej stronie, z dala od treści - to koszmar czytelności.

Rozwiązaniem dla każdego, kto szuka zdrowego rozsądku, jest

  1. uzyskać prawdziwy diff (np. z GnuWin32)
  2. edycja% USERPROFILE% \ Documents \ WindowsPowerShell \ Microsoft.PowerShell_profile.ps1
  3. dodaj linię

    remove-item alias:diff -force

Argument -force jest wymagany, ponieważ Powershell jest dość cenny w przypadku tego konkretnego wbudowanego aliasu. Jeśli ktoś jest zainteresowany, po zainstalowaniu GnuWin32, dołączam również następujące informacje do mojego profilu PowerShell:

remove-item alias:rm
remove-item alias:mv
remove-item alias:cp

Głównie dlatego, że Powershell nie rozumie argumentów uruchamianych razem i pisania, na przykład „rm -Force -Recurse” jest znacznie większym wysiłkiem niż „rm -rf”.

Program PowerShell ma kilka fajnych funkcji, ale jest kilka rzeczy, których nie powinien po prostu dla mnie robić.

daf
źródło
2

WinMerge to kolejne dobre narzędzie do porównywania oparte na GUI.

Andy White
źródło
1
Tak właśnie robiłem w przeszłości, który jest procesem ręcznym, który chciałem zastąpić małym skryptem.
Bratch
1

Istnieje również Windiff, który zapewnia interfejs GUI (świetny do użytku z programami CVS / SVN opartymi na GUI)

saschabeaumont
źródło
1

fc.exejest lepszy do porównywania tekstu, ponieważ zaprojektowano go do działania jak * nix diff, tzn. porównuje linie sekwencyjnie, pokazując rzeczywiste różnice i próbując ponownie zsynchronizować (jeśli różne sekcje mają różne długości). Ma także kilka przydatnych opcji sterowania (tekst / binarny, rozróżnianie wielkości liter, numery linii, długość resynchronizacji, rozmiar bufora niedopasowania) i zapewnia status wyjścia (-1 zła składnia, 0 takich samych plików, 1 pliki różnią się, brak 2 plików). Jako (bardzo) stare narzędzie DOS ma kilka ograniczeń. Co najważniejsze, nie działa on automatycznie z Unicode, traktując 0 MSB znaków ASCII jako terminator linii, więc plik staje się sekwencją 1 linii znaków (@kennycoc: użyj opcji / U, aby określić, że OBIE pliki są Unicode, a WinXP i nowsze ), a także ma sztywny bufor o rozmiarze 128 znaków (128 bajtów ASCII,

Obiekt porównywania służy do ustalenia, czy 2 obiekty są identyczne pod względem członków. jeśli obiekty są kolekcjami, to są one traktowane jako ZESTAWY (patrz pomoc porównania-obiekt), tj. kolekcje NIEZGODNE bez duplikatów. 2 zestawy są równe, jeśli mają takie same elementy członkowskie, niezależnie od kolejności lub duplikatów. To poważnie ogranicza jego przydatność do porównywania plików tekstowych pod kątem różnic. Po pierwsze, domyślne zachowanie zbiera różnice, dopóki cały obiekt (plik = tablica ciągów) nie zostanie sprawdzony, tracąc w ten sposób informacje dotyczące położenia różnic i zaciemniając, które różnice są sparowane (i nie ma pojęcia numeru linii dla SET ciągów). Użycie opcji -synchwindow 0 spowoduje, że różnice zostaną wyemitowane w momencie ich wystąpienia, ale powstrzyma ją od ponownej synchronizacji, więc jeśli jeden plik ma dodatkową linię, kolejne porównania linii mogą się nie powieść, nawet jeśli pliki są identyczne (dopóki nie pojawi się kompensacja dodatkowa linia w drugim pliku, wyrównując w ten sposób pasujące linie). Jednak PowerShell jest niezwykle wszechstronny, a przydatne porównanie plików można wykonać, korzystając z tej funkcji, aczkolwiek kosztem znacznej złożoności i pewnych ograniczeń dotyczących zawartości plików. Jeśli chcesz porównać pliki tekstowe z długimi (> 127 znaków) liniami i których linie w większości pasują do 1:

diff (gc file1 | % -begin { $ln1=0 } -process { '{0,6}<<:{1}' -f ++$ln1,$_ }) (gc file2 | % -begin { $ln2=0 } -process { '{0,6}>>:{1}' -f ++$ln2,$_ }) -property { $_.substring(9) } -passthru | sort | out-string -width xx

gdzie xx jest długością najdłuższej linii + 9

Wyjaśnienie

  • (gc file | % -begin { $ln=0 } -process { '{0,6}<<:{1}' -f ++$ln,$_ }) pobiera zawartość pliku i wstawia numer linii i wskaźnik pliku (<< lub >>) do każdej linii (używając operatora ciągu formatu) przed przekazaniem jej do pliku diff.
  • -property { $_.substring(9) }każe diffowi porównać każdą parę obiektów (ciągów znaków) ignorując pierwsze 9 znaków (które są numerem linii i wskaźnikiem pliku). Wykorzystuje to możliwość określenia obliczonej właściwości (wartości bloku skryptu) zamiast nazwy właściwości.
  • -passthru powoduje, że diff wypisuje różne obiekty wejściowe (które obejmują numer linii i wskaźnik pliku) zamiast różnych porównywanych obiektów (które nie).
  • sort-objectnastępnie ustawia wszystkie linie z powrotem w sekwencji.
    out-string zatrzymuje domyślne obcięcie wyjścia w celu dopasowania do szerokości ekranu (jak zauważył Marc Towersap) przez określenie szerokości wystarczająco dużej, aby uniknąć obcięcia. Zwykle dane wyjściowe byłyby umieszczane w pliku, który jest następnie przeglądany za pomocą przewijanego edytora (np. Notatnika).

Uwaga

Format numeru linii {0,6} daje wyrównany do prawej 6-znakowy numer wiersza (do sortowania). Jeśli pliki mają więcej niż 999,999 wierszy, po prostu zmień format na szerszy. Wymaga to również zmiany $_.substringparametru (3 więcej niż szerokość numeru linii) i wartości x-out-string (maksymalna długość linii + $_.substringparametr).

kodemaster bob
źródło