だから基本的に私がしたいのは、2つのファイルを行2ごとに比較することです。
File_1.txt:
User1 US
User2 US
User3 US
File_2.txt:
User1 US
User2 US
User3 NG
Output_File:
User3 has changed
だから基本的に私がしたいのは、2つのファイルを行2ごとに比較することです。
File_1.txt:
User1 US
User2 US
User3 US
File_2.txt:
User1 US
User2 US
User3 NG
Output_File:
User3 has changed
回答:
diff
コマンドを調べてください。これは優れたツールでありman diff
、ターミナルに入力することですべてを読むことができ ます。
実行するコマンドdiff File_1.txt File_2.txt
は、2つの間の差を出力し、次のようになります。
3番目のコマンドからの出力の読み取りに関する簡単な注意:「矢印」(<
および>
)は、左側のファイル(<
)と右側のファイル(>
)の行の値を示し、左側のファイルは入力したものですこの場合、最初にコマンドラインでFile_1.txt
さらに、4番目のコマンドがdiff ... | tee Output_File
から結果diff
をaにパイプし、tee
その出力をファイルに書き込むことに気付くかもしれません。その出力をすぐにコンソールに表示したくない場合は、後で保存できるようになります。
diff file1 file2 -s
。次に例を示します。imgur.com
cmp
diff
戻りコードだけが必要な場合よりもはるかに高速です。
質問(file1、file2、「has changed」メッセージを含むoutputfile)にこだわる以下のスクリプトが機能します。
スクリプトを空のファイルにコピーし、名前を付けて保存しcompare.py
、実行可能にし、コマンドで実行します。
/path/to/compare.py <file1> <file2> <outputfile>
スクリプト:
#!/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")
数行追加することで、出力ファイルが定義されているかどうかに応じて、出力ファイルまたは端末に出力できます。
ファイルに印刷するには:
/path/to/compare.py <file1> <file2> <outputfile>
端末ウィンドウに印刷するには:
/path/to/compare.py <file1> <file2>
スクリプト:
#!/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"
簡単な方法は、を使用することですcolordiff
。これはdiff
、出力のように動作しますが、出力を色付けします。これは差分を読むのに非常に役立ちます。あなたの例を使用して、
$ 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
u
オプションは統一された差分を提供します。これは、色付けされた差分がどのように見えるかです:
をcolordiff
実行してインストールしますsudo apt-get install colordiff
。
ファイルのどの部分が異なるかを知る必要がない場合は、ファイルのチェックサムを使用できます。md5sum
またはを使用して、これを行う方法は多数ありますsha256sum
。基本的に、それらはそれぞれファイル内容がハッシュする文字列を出力します。2つのファイルが同じ場合、それらのハッシュも同じになります。これは、Ubuntuインストールisoイメージなどのソフトウェアをダウンロードするときによく使用されます。多くの場合、ダウンロードされたコンテンツの整合性を検証するために使用されます。
以下のスクリプトを検討してください。ここでは、2つのファイルを引数として指定できます。ファイルは、それらが同じかどうかを示します。
#!/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
サンプル実行:
$ ./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
さらにcomm
、2つのソートされたファイルを比較し、3列に出力するコマンドがあります。列1はファイル#1に固有のアイテム、列2はファイル#2に固有のアイテム、列3は両方のファイルに存在します。
いずれかの列を抑制するには、スイッチ-1、-2、および-3を使用できます。-3を使用すると、異なる行が表示されます。
以下に、実行中のコマンドのスクリーンショットを見ることができます。
要件は1つだけです。ファイルを適切に比較するには、ファイルを並べ替える必要があります。sort
コマンドはその目的に使用できます。以下は、ファイルを並べ替えて比較する別のスクリーンショットです。File_1への左側の先頭から始まる行のみ、列2から始まる行はFile_2のみに属します
形式の2つのファイルの名前/値のペアを比較しますname value\n
。変更さname
れたOutput_file
場合にtoを書き込みます。連想配列には bash v4 +が必要です。
$ ./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
コードの内訳とその意味を、できる限り理解してください。編集や提案を歓迎します。
cmp -s "$1" "$2"
case "$?" in
0)
# match
;;
1)
# compare
;;
*)
# error
;;
esac
私が使用することを選択した場合 ... ESACの evaluteする文を$?なぜなら$の値は?テスト([)を含むすべてのコマンドの後に変更されます。
cmp -s "$1" "$2"
CMPRESULT=$?
if [ $CMPRESULT -eq 0 ]; then
# match
elif [ $CMPRESULT -eq 1 ]; then
# compare
else
# error
fi
上記は、caseステートメントと同じことを行います。私が好きなIDK。
echo "" > Output_File
上記は出力ファイルをクリアするため、ユーザーが変更されていない場合、出力ファイルは空になります。
エラー時にOutput_fileが変更されないように、caseステートメント内でこれを行います。
cp "$1" ~/.colcmp.arrays.tmp.sh
上記はFile_1.txtを現在のユーザーのホームディレクトリにコピーします。
たとえば、現在のユーザーがjohnの場合、上記はcp "File_1.txt" /home/john/.colcmp.arrays.tmp.shと同じになります。
基本的に、私は妄想です。これらの文字は、変数の割り当ての一部としてスクリプトで実行されると、特別な意味を持つか、外部プログラムを実行できることを知っています。
私が知らないのは、bashについてどれだけ知らないかです。他の文字が特別な意味を持っているかどうかはわかりませんが、バックスラッシュですべてをエスケープします。
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sedは、正規表現のパターンマッチングよりも多くのことができます。スクリプトパターン「s /(find)/(replace)/」は、特にパターンマッチを実行します。
「s /(find)/(replace)/(modifiers)」
英語:句読点または特殊文字をキャプチャグループ1としてキャプチャします(\\ 1)
英語:すべての特殊文字の前にバックスラッシュを付けます
英語:同じ行に複数の一致が見つかった場合、それらをすべて置き換えます
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.arrays.tmp.sh
上記では、正規表現を使用して〜/ .colcmp.arrays.tmp.shのすべての行の先頭にbashコメント文字(#)を付けています。これは、後でsourceコマンドを使用して〜/ .colcmp.arrays.tmp.shを実行する予定であり、File_1.txtの形式全体が不明なためです。
誤って任意のコードを実行したくない。誰もそうは思わない。
「s /(検索)/(置換)/」
英語:各行をキャプチャグループ1としてキャプチャします(\\ 1)
英語:各行をポンド記号で置き換え、その後に置き換えられた行を置きます
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
上記はこのスクリプトの中核です。
#User1 US
A1[User1]="US"
A2[User1]="US"
(2番目のファイルの場合)「s /(検索)/(置換)/」
英語で:
残りの行をキャプチャグループ2としてキャプチャします
(置換)= A1 \\ [\\ 1 \\] = \ "\\ 2 \"
A1[
と呼ばれる配列で配列の割り当てを開始するリテラル文字A1
]="
]
=配列の割り当てを閉じる(例:A1[
User1 ]="
US)"
=
=代入演算子、たとえばvariable = value"
=空白をキャプチャするための値を引用する...考えてみると、上記のコードですべてをバックスラッシュしてスペース文字もバックスラッシュする方が簡単だったはずです。英語:形式の各行を形式#name value
の配列代入演算子に置き換えますA1[name]="value"
chmod 755 ~/.colcmp.arrays.tmp.sh
上記では、chmodを使用して、アレイスクリプトファイルを実行可能にします。
これが必要かどうかはわかりません。
declare -A A1
大文字の-Aは、宣言された変数が連想配列になることを示します。
これが、スクリプトにbash v4以降が必要な理由です。
source ~/.colcmp.arrays.tmp.sh
我々はすでに持っています:
User value
のラインにA1[User]="value"
、上記では、現在のシェルでスクリプトを実行するためのソースを用意しています。これにより、スクリプトによって設定される変数値を保持できます。スクリプトを直接実行すると、新しいシェルが生成され、新しいシェルが終了すると変数値が失われます。少なくとも、それは私の理解です。
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
$ 1とA1に対しても、$ 2とA2に対して行うのと同じことを行います。それは本当に関数でなければなりません。この時点で、このスクリプトは十分に混乱し、機能していると思うので、修正するつもりはありません。
for i in "${!A1[@]}"; do
# check for users removed
done
上記の連想配列キーのループ
if [ "${A2[$i]+x}" = "" ]; then
上記では、変数置換を使用して、未設定の値と長さゼロの文字列に明示的に設定された変数の違いを検出します。
どうやら、変数が設定されているかどうかを確認する方法はたくさんあります。投票数が最も多いものを選びました。
echo "$i has changed" > Output_File
上記はユーザー$ iをOutput_Fileに追加します
USERSWHODIDNOTCHANGE=
上記は変数をクリアするため、変更されなかったユーザーを追跡できます。
for i in "${!A2[@]}"; do
# detect users added, changed and not changed
done
上記の連想配列キーのループ
if ! [ "${A1[$i]+x}" != "" ]; then
上記は変数置換を使用して、変数が設定されているかどうかを確認します。
echo "$i was added as '${A2[$i]}'"
ので、$ iの配列のキー(ユーザー名)$ A2 [$ i]がから現在のユーザーに関連付けられている値を返す必要がありますですFile_2.txtを。
たとえば、$ iがUser1の場合、上記は$ {A2 [User1]}として読み取ります
echo "$i has changed" > Output_File
上記はユーザー$ iをOutput_Fileに追加します
elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
ので、$ iの配列のキー(ユーザー名)$ A1 [$ i]がから現在のユーザーに関連付けられている値を返す必要がありますですFile_1.txtを、そして$ A2 [$ i]はから値を返す必要がありFile_2.txt。
上記は、両方のファイルのユーザー$ iに関連付けられた値を比較しています。
echo "$i has changed" > Output_File
上記はユーザー$ iをOutput_Fileに追加します
if [ x$USERSWHODIDNOTCHANGE != x ]; then
USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
fi
USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
上記は、変更しなかったユーザーのコンマ区切りリストを作成します。リストにはスペースがないことに注意してください。スペースがない場合、次のチェックを引用符で囲む必要があります。
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
上記の値が報告$ USERSWHODIDNOTCHANGEをが、価値がある場合のみ$ USERSWHODIDNOTCHANGE。これを記述する方法、$ USERSWHODIDNOTCHANGEにスペースを含めることはできません。スペースが必要な場合は、上記を次のように書き換えることができます。
if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
diff "File_1.txt" "File_2.txt"