Vì vậy, về cơ bản những gì tôi muốn làm là so sánh hai tệp theo từng dòng 2. Làm thế nào tôi có thể thực hiện được điều này?
Tệp_1.txt:
User1 US
User2 US
User3 US
Tệp_2.txt:
User1 US
User2 US
User3 NG
Kết quả đầu ra:
User3 has changed
Vì vậy, về cơ bản những gì tôi muốn làm là so sánh hai tệp theo từng dòng 2. Làm thế nào tôi có thể thực hiện được điều này?
Tệp_1.txt:
User1 US
User2 US
User3 US
Tệp_2.txt:
User1 US
User2 US
User3 NG
Kết quả đầu ra:
User3 has changed
Câu trả lời:
Nhìn vào diff
mệnh lệnh. Đây là một công cụ tốt và bạn có thể đọc tất cả về nó bằng cách nhập man diff
vào thiết bị đầu cuối của bạn.
Lệnh bạn sẽ muốn làm là diff File_1.txt File_2.txt
sẽ tạo ra sự khác biệt giữa hai và sẽ trông giống như thế này:
Lưu ý nhanh về việc đọc đầu ra từ lệnh thứ ba: 'Mũi tên' ( <
và >
) đề cập đến giá trị của dòng trong tệp bên trái ( <
) so với tệp bên phải ( >
), với tệp bên trái là tệp bạn đã nhập đầu tiên trên dòng lệnh, trong trường hợp nàyFile_1.txt
Ngoài ra, bạn có thể nhận thấy lệnh thứ 4 là diff ... | tee Output_File
điều này dẫn các kết quả từ diff
một tee
, sau đó đưa đầu ra đó vào một tệp, để bạn có thể lưu nó sau này nếu bạn không muốn xem tất cả trên bàn điều khiển ngay lúc đó.
diff file1 file2 -s
. Dưới đây là một ví dụ: imgur.com/ShrQx9x
Hoặc bạn có thể sử dụng Meld Diff
Meld giúp bạn so sánh các tập tin, thư mục và các dự án được kiểm soát phiên bản. Nó cung cấp so sánh hai và ba chiều của cả tệp và thư mục và có hỗ trợ cho nhiều hệ thống kiểm soát phiên bản phổ biến.
Cài đặt bằng cách chạy:
sudo apt-get install meld
Ví dụ của bạn:
So sánh thư mục:
Ví dụ với đầy đủ văn bản:
dos
và thứ hai trong unix
.
FWIW, tôi thích những gì tôi nhận được với đầu ra song song từ diff
diff -y -W 120 File_1.txt File_2.txt
sẽ cung cấp một cái gì đó như:
User1 US User1 US
User2 US User2 US
User3 US | User3 NG
Bạn có thể sử dụng lệnh cmp
:
cmp -b "File_1.txt" "File_2.txt"
đầu ra sẽ là
a b differ: byte 25, line 3 is 125 U 116 N
cmp
nhanh hơn nhiều so với diff
nếu tất cả những gì bạn muốn là mã trả về.
Litteraly dính vào câu hỏi (file1, file2, outputfile với thông báo "đã thay đổi") đoạn script bên dưới hoạt động.
Sao chép tập lệnh vào một tập tin trống, lưu nó dưới dạng compare.py
, làm cho nó có thể thực thi được, chạy nó bằng lệnh:
/path/to/compare.py <file1> <file2> <outputfile>
Kịch bản:
#!/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")
Với một vài dòng bổ sung, bạn có thể làm cho nó in ra tệp đầu ra hoặc tới thiết bị đầu cuối, tùy thuộc vào việc tệp đầu ra được xác định:
Để in thành tập tin:
/path/to/compare.py <file1> <file2> <outputfile>
Để in ra cửa sổ terminal:
/path/to/compare.py <file1> <file2>
Kịch bản:
#!/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"
Một cách dễ dàng là sử dụng colordiff
, nó hoạt động như thế diff
nhưng tô màu đầu ra của nó. Điều này rất hữu ích cho việc đọc khác biệt. Sử dụng ví dụ của bạn,
$ 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
trong đó u
tùy chọn cho một sự khác biệt thống nhất. Đây là cách diff khác màu trông như thế nào:
Cài đặt colordiff
bằng cách chạy sudo apt-get install colordiff
.
Nếu không cần biết phần nào của tệp khác nhau, bạn có thể sử dụng tổng kiểm tra của tệp. Có nhiều cách để làm điều đó, sử dụng md5sum
hoặc sha256sum
. Về cơ bản, mỗi người trong số họ đưa ra một chuỗi mà nội dung tệp băm. Nếu hai tệp giống nhau, hàm băm của chúng cũng sẽ giống nhau. Điều này thường được sử dụng khi bạn tải xuống phần mềm, chẳng hạn như hình ảnh iso cài đặt Ubuntu. Chúng thường được sử dụng để xác minh tính toàn vẹn của nội dung được tải xuống.
Xem xét tập lệnh bên dưới, nơi bạn có thể đưa ra hai tệp làm đối số và tệp sẽ cho bạn biết liệu chúng có giống nhau hay không.
#!/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
Chạy mẫu:
$ ./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
Ngoài ra, còn có comm
lệnh so sánh hai tệp được sắp xếp và đưa ra kết quả trong 3 colums: cột 1 cho các mục duy nhất cho tệp # 1, cột 2 cho các mục duy nhất cho tệp # 2 và cột 3 cho các mục có trong cả hai tệp.
Để chặn một trong hai cột, bạn có thể sử dụng các công tắc -1, -2 và -3. Sử dụng -3 sẽ hiển thị các dòng khác nhau.
Dưới đây bạn có thể thấy ảnh chụp màn hình của lệnh đang hoạt động.
Chỉ có một yêu cầu - các tệp phải được sắp xếp để chúng được so sánh đúng. sort
lệnh có thể được sử dụng cho mục đích đó. Bellow là một ảnh chụp màn hình khác, nơi các tệp được sắp xếp và sau đó so sánh. Các dòng bắt đầu ở bên trái chỉ đến File_1, các dòng bắt đầu trên cột 2 chỉ thuộc về File_2
Cài đặt git và sử dụng
$ git diff filename1 filename2
Và bạn sẽ nhận được đầu ra ở định dạng màu đẹp
Cài đặt Git
$ apt-get update
$ apt-get install git-core
So sánh các cặp tên / giá trị trong 2 tệp ở định dạng name value\n
. Viết name
đến Output_file
nếu thay đổi. Yêu cầu bash v4 + cho các mảng kết hợp .
$ ./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
Phân tích mã và ý nghĩa của nó, theo sự hiểu biết tốt nhất của tôi. Tôi hoan nghênh các chỉnh sửa và đề xuất.
cmp -s "$1" "$2"
case "$?" in
0)
# match
;;
1)
# compare
;;
*)
# error
;;
esac
cmp sẽ đặt giá trị của $? như sau :
Tôi đã chọn sử dụng một trường hợp .. tuyên bố esac để đánh giá $? vì giá trị của $? thay đổi sau mỗi lệnh, bao gồm kiểm tra ([).
Ngoài ra, tôi có thể sử dụng một biến để giữ giá trị $? :
cmp -s "$1" "$2"
CMPRESULT=$?
if [ $CMPRESULT -eq 0 ]; then
# match
elif [ $CMPRESULT -eq 1 ]; then
# compare
else
# error
fi
Ở trên làm điều tương tự như tuyên bố trường hợp. IDK mà tôi thích hơn.
echo "" > Output_File
Ở trên xóa tệp đầu ra để nếu không có người dùng thay đổi, tệp đầu ra sẽ trống.
Tôi thực hiện điều này bên trong các báo cáo trường hợp để Output_file không thay đổi do lỗi.
cp "$1" ~/.colcmp.arrays.tmp.sh
Trên bản sao File_1.txt vào thư mục nhà hiện tại của người dùng.
Ví dụ: nếu người dùng hiện tại là john, thì ở trên sẽ giống như cp "File_1.txt" /home/john/.colcmp.arrays.tmp.sh
Về cơ bản, tôi bị hoang tưởng. Tôi biết rằng các ký tự này có thể có ý nghĩa đặc biệt hoặc thực thi chương trình bên ngoài khi chạy trong tập lệnh như là một phần của phép gán biến:
Những gì tôi không biết là tôi không biết bao nhiêu về bash. Tôi không biết những nhân vật khác có thể có ý nghĩa đặc biệt gì, nhưng tôi muốn thoát khỏi tất cả chúng bằng dấu gạch chéo ngược:
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed có thể làm nhiều hơn so với khớp mẫu biểu thức thông thường . Mẫu tập lệnh "s / (find) / (thay thế) /" đặc biệt thực hiện khớp mẫu.
"s / (tìm) / (thay thế) / (sửa đổi)"
bằng tiếng Anh: nắm bắt bất kỳ dấu câu hoặc ký tự đặc biệt nào trong nhóm caputure 1 (\\ 1)
trong tiếng Anh: tiền tố tất cả các ký tự đặc biệt có dấu gạch chéo ngược
trong tiếng Anh: nếu tìm thấy nhiều hơn một trận đấu trên cùng một dòng, thay thế tất cả chúng
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.arrays.tmp.sh
Ở trên sử dụng một biểu thức chính quy để tiền tố mỗi dòng ~ / .colcmp.arrays.tmp.sh với một ký tự nhận xét bash ( # ). Tôi làm điều này bởi vì sau này tôi có ý định thực thi ~ / .colcmp.arrays.tmp.sh bằng cách sử dụng lệnh nguồn và vì tôi không biết chắc chắn toàn bộ định dạng của File_1.txt .
Tôi không muốn vô tình thực thi mã tùy ý. Tôi không nghĩ có ai làm thế.
"s / (tìm) / (thay thế) /"
bằng tiếng Anh: chụp từng dòng dưới dạng nhóm caputure 1 (\\ 1)
trong tiếng Anh: thay thế mỗi dòng bằng ký hiệu pound theo sau là dòng được thay thế
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
Trên đây là cốt lõi của kịch bản này.
#User1 US
A1[User1]="US"
A2[User1]="US"
(cho tệp thứ 2)"s / (tìm) / (thay thế) /"
bằng tiếng Anh:
bắt phần còn lại của dòng là bắt nhóm 2
(thay thế) = A1 \\ [\\ 1 \\] = \ "\\ 2 \"
A1[
để bắt đầu gán mảng trong một mảng được gọi làA1
]="
]
= đóng mảng gán, ví dụ A1[
User1 ]="
US"
=
= toán tử gán, ví dụ biến = value"
= trích dẫn giá trị để chụp khoảng trắng ... mặc dù bây giờ tôi nghĩ về nó, sẽ dễ dàng hơn khi để đoạn mã phía trên làm ngược lại mọi thứ để gạch chéo các ký tự khoảng trắng.bằng tiếng Anh: thay thế từng dòng trong định dạng #name value
bằng toán tử gán mảng theo định dạngA1[name]="value"
chmod 755 ~/.colcmp.arrays.tmp.sh
Ở trên sử dụng chmod để làm cho tập tin mảng tập lệnh thực thi.
Tôi không chắc chắn nếu điều này là cần thiết.
declare -A A1
Vốn -A chỉ ra rằng các biến được khai báo sẽ là mảng kết hợp .
Đây là lý do tại sao tập lệnh yêu cầu bash v4 trở lên.
source ~/.colcmp.arrays.tmp.sh
Chúng tôi đã sẵn sàng:
User value
dòng A1[User]="value"
,Ở trên chúng tôi nguồn kịch bản để chạy nó trong shell hiện tại. Chúng tôi làm điều này để chúng tôi có thể giữ các giá trị biến được đặt bởi tập lệnh. Nếu bạn thực thi trực tiếp tập lệnh, nó sẽ sinh ra một lớp vỏ mới và các giá trị biến bị mất khi lớp vỏ mới thoát ra, hoặc ít nhất đó là sự hiểu biết của tôi.
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
Chúng tôi làm điều tương tự với $ 1 và A1 mà chúng tôi làm với $ 2 và A2 . Nó thực sự nên là một chức năng. Tôi nghĩ tại thời điểm này kịch bản này đủ khó hiểu và nó hoạt động, vì vậy tôi sẽ không sửa nó.
for i in "${!A1[@]}"; do
# check for users removed
done
Trên vòng lặp thông qua các khóa mảng kết hợp
if [ "${A2[$i]+x}" = "" ]; then
Ở trên sử dụng thay thế biến để phát hiện sự khác biệt giữa một giá trị không được đặt so với biến đã được đặt rõ ràng thành chuỗi có độ dài bằng không.
Rõ ràng, có rất nhiều cách để xem nếu một biến đã được đặt . Tôi đã chọn một người có nhiều phiếu nhất.
echo "$i has changed" > Output_File
Ở trên, thêm người dùng $ i vào Output_File
USERSWHODIDNOTCHANGE=
Ở trên xóa một biến để chúng tôi có thể theo dõi người dùng không thay đổi.
for i in "${!A2[@]}"; do
# detect users added, changed and not changed
done
Trên vòng lặp thông qua các khóa mảng kết hợp
if ! [ "${A1[$i]+x}" != "" ]; then
Ở trên sử dụng thay thế biến để xem nếu một biến đã được đặt .
echo "$i was added as '${A2[$i]}'"
Vì $ i là khóa mảng (tên người dùng) $ A2 [$ i] nên trả về giá trị được liên kết với người dùng hiện tại từ File_2.txt .
Ví dụ: nếu $ i là User1 , thì ở trên đọc là $ {A2 [User1]}
echo "$i has changed" > Output_File
Ở trên, thêm người dùng $ i vào Output_File
elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
Vì $ i là khóa mảng (tên người dùng) $ A1 [$ i] nên trả về giá trị được liên kết với người dùng hiện tại từ File_1.txt và $ A2 [$ i] sẽ trả về giá trị từ File_2.txt .
Ở trên so sánh các giá trị liên quan cho người dùng $ i từ cả hai tệp ..
echo "$i has changed" > Output_File
Ở trên, thêm người dùng $ i vào Output_File
if [ x$USERSWHODIDNOTCHANGE != x ]; then
USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
fi
USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
Ở trên tạo một danh sách người dùng được phân tách bằng dấu phẩy, những người không thay đổi. Lưu ý rằng không có khoảng trắng trong danh sách, nếu không thì kiểm tra tiếp theo sẽ cần được trích dẫn.
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
Ở trên báo cáo giá trị của $ USERSWHODIDNOTCHANGE nhưng chỉ khi có một giá trị trong $ USERSWHODIDNOTCHANGE . Cách viết này, $ USERSWHODIDNOTCHANGE không thể chứa bất kỳ khoảng trắng nào. Nếu nó cần khoảng trắng, ở trên có thể được viết lại như sau:
if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
diff "File_1.txt" "File_2.txt"