colcmp.sh
Membandingkan pasangan nama / nilai dalam 2 file dalam format name value\n
. Menulis name
untuk Output_file
jika diubah. Membutuhkan bash v4 + untuk array asosiatif .
Pemakaian
$ ./colcmp.sh File_1.txt File_2.txt
User3 changed from 'US' to 'NG'
no change: User1,User2
Berkas keluaran
$ cat Output_File
User3 has changed
Sumber (colcmp.sh)
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
Penjelasan
Rincian kode dan apa artinya, sejauh yang saya mengerti. Saya menerima suntingan dan saran.
Bandingkan File Dasar
cmp -s "$1" "$2"
case "$?" in
0)
# match
;;
1)
# compare
;;
*)
# error
;;
esac
cmp akan menetapkan nilai $? sebagai berikut :
- 0 = file cocok
- 1 = file berbeda
- 2 = kesalahan
Saya memilih untuk menggunakan case .. pernyataan esac untuk evalute $? karena nilai $? berubah setelah setiap perintah, termasuk tes ([).
Atau saya bisa menggunakan variabel untuk menyimpan nilai $? :
cmp -s "$1" "$2"
CMPRESULT=$?
if [ $CMPRESULT -eq 0 ]; then
# match
elif [ $CMPRESULT -eq 1 ]; then
# compare
else
# error
fi
Di atas melakukan hal yang sama dengan pernyataan kasus. IDK yang saya sukai lebih baik.
Bersihkan Output
echo "" > Output_File
Di atas menghapus file output jadi jika tidak ada pengguna yang berubah, file output akan kosong.
Saya melakukan ini di dalam pernyataan kasus sehingga Output_file tetap tidak berubah pada kesalahan.
Salin File Pengguna ke Shell Script
cp "$1" ~/.colcmp.arrays.tmp.sh
Di atas menyalin File_1.txt ke direktori home pengguna saat ini.
Misalnya, jika pengguna saat ini adalah john, di atas akan sama dengan cp "File_1.txt" /home/john/.colcmp.arrays.tmp.sh
Escape Karakter Khusus
Pada dasarnya, saya paranoid. Saya tahu bahwa karakter ini dapat memiliki arti khusus atau menjalankan program eksternal ketika dijalankan dalam skrip sebagai bagian dari penugasan variabel:
- `- centang-ulang - menjalankan program dan output seolah-olah output adalah bagian dari skrip Anda
- Tanda $ - dolar - biasanya awalan variabel
- $ {} - memungkinkan untuk substitusi variabel yang lebih kompleks
- $ () - idk apa ini tetapi saya pikir itu dapat mengeksekusi kode
Yang saya tidak tahu adalah seberapa banyak saya tidak tahu tentang bash. Saya tidak tahu apa karakter lain yang mungkin memiliki makna khusus, tetapi saya ingin menghindarinya dengan backslash:
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed dapat melakukan lebih dari sekadar pencocokan pola ekspresi reguler . Pola skrip "s / (find) / (ganti) /" secara khusus melakukan kecocokan pola.
"s / (temukan) / (ganti) / (pengubah)"
- (temukan) = ([^ A-Za-z0-9])
dalam bahasa Inggris: tangkap tanda baca atau karakter khusus apa pun sebagai grup caputure 1 (\\ 1)
- (ganti) = \\ 1
- \\ = karakter literal (\\) yaitu garis miring terbalik
- \\ 1 = kelompok tangkap 1
dalam bahasa Inggris: awali semua karakter khusus dengan garis miring terbalik
dalam bahasa Inggris: jika lebih dari satu kecocokan ditemukan pada baris yang sama, gantilah semuanya
Komentari Seluruh Script
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.arrays.tmp.sh
Di atas menggunakan ekspresi reguler untuk mengawali setiap baris ~ / .colcmp.arrays.tmp.sh dengan karakter komentar bash ( # ). Saya melakukan ini karena nanti saya bermaksud untuk mengeksekusi ~ / .colcmp.arrays.tmp.sh menggunakan perintah sumber dan karena saya tidak tahu pasti seluruh format File_1.txt .
Saya tidak ingin secara tidak sengaja mengeksekusi kode arbitrer. Saya tidak berpikir ada yang melakukannya.
"s / (temukan) / (ganti) /"
dalam bahasa Inggris: tangkap setiap baris sebagai grup caputure 1 (\\ 1)
dalam bahasa Inggris: ganti setiap baris dengan simbol pound diikuti dengan garis yang diganti
Konversi Nilai Pengguna ke A1 [Pengguna] = "nilai"
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh
Di atas adalah inti dari skrip ini.
- ubah ini:
#User1 US
- untuk ini:
A1[User1]="US"
- atau ini:
A2[User1]="US"
(untuk file ke-2)
"s / (temukan) / (ganti) /"
- (temukan) = ^ # \\ s * (\\ S +) \\ s + (\\ S. ?) \\ s \ $
dalam Bahasa Inggris:
dalam bahasa Inggris: ganti setiap baris dalam format #name value
dengan operator penugasan array dalam formatA1[name]="value"
Jadikan Dapat Dieksekusi
chmod 755 ~/.colcmp.arrays.tmp.sh
Di atas menggunakan chmod untuk membuat file skrip array dapat dieksekusi.
Saya tidak yakin apakah ini perlu.
Deklarasikan Array Asosiatif (bash v4 +)
declare -A A1
Capital -A menunjukkan bahwa variabel yang dideklarasikan akan menjadi array asosiatif .
Inilah sebabnya mengapa skrip membutuhkan bash v4 atau lebih tinggi.
Jalankan Script Assignment Variable Assignment kami
source ~/.colcmp.arrays.tmp.sh
Kita sudah:
- mengkonversi file kami dari baris
User value
ke baris A1[User]="value"
,
- menjadikannya executable (mungkin), dan
- menyatakan A1 sebagai array asosiatif ...
Di atas kami sumber script untuk menjalankannya di shell saat ini Kami melakukan ini agar kami dapat menyimpan nilai variabel yang ditetapkan oleh skrip. Jika Anda menjalankan skrip secara langsung, itu memunculkan shell baru, dan nilai-nilai variabel hilang ketika shell baru keluar, atau setidaknya itu adalah pemahaman saya.
Ini Seharusnya Berfungsi
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
Kami melakukan hal yang sama untuk $ 1 dan A1 yang kami lakukan untuk $ 2 dan A2 . Itu benar-benar harus menjadi fungsi. Saya pikir pada titik ini skrip ini cukup membingungkan dan berfungsi, jadi saya tidak akan memperbaikinya.
Deteksi Pengguna Dihapus
for i in "${!A1[@]}"; do
# check for users removed
done
Di atas loop melalui kunci array asosiatif
if [ "${A2[$i]+x}" = "" ]; then
Di atas menggunakan substitusi variabel untuk mendeteksi perbedaan antara nilai yang tidak disetel vs variabel yang telah secara eksplisit diatur ke string panjang nol.
Rupanya, ada banyak cara untuk melihat apakah suatu variabel telah ditetapkan . Saya memilih satu dengan suara terbanyak.
echo "$i has changed" > Output_File
Di atas menambahkan pengguna $ i ke Output_File
Deteksi Pengguna Ditambahkan atau Diubah
USERSWHODIDNOTCHANGE=
Di atas menghapus variabel sehingga kami dapat melacak pengguna yang tidak berubah.
for i in "${!A2[@]}"; do
# detect users added, changed and not changed
done
Di atas loop melalui kunci array asosiatif
if ! [ "${A1[$i]+x}" != "" ]; then
Di atas menggunakan substitusi variabel untuk melihat apakah suatu variabel telah ditetapkan .
echo "$i was added as '${A2[$i]}'"
Karena $ i adalah kunci array (nama pengguna) $ A2 [$ i] harus mengembalikan nilai yang terkait dengan pengguna saat ini dari File_2.txt .
Misalnya, jika $ i adalah User1 , yang di atas berbunyi $ {A2 [User1]}
echo "$i has changed" > Output_File
Di atas menambahkan pengguna $ i ke Output_File
elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
Karena $ i adalah kunci array (nama pengguna) $ A1 [$ i] harus mengembalikan nilai yang terkait dengan pengguna saat ini dari File_1.txt , dan $ A2 [$ i] harus mengembalikan nilai dari File_2.txt .
Di atas membandingkan nilai yang terkait untuk pengguna $ i dari kedua file ..
echo "$i has changed" > Output_File
Di atas menambahkan pengguna $ i ke Output_File
if [ x$USERSWHODIDNOTCHANGE != x ]; then
USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
fi
USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
Di atas membuat daftar pengguna yang dipisahkan koma dan tidak berubah. Perhatikan tidak ada spasi dalam daftar, atau cek berikutnya perlu dikutip.
if [ x$USERSWHODIDNOTCHANGE != x ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
Di atas melaporkan nilai $ USERSWHODIDNOTCHANGE tetapi hanya jika ada nilai dalam $ USERSWHODIDNOTCHANGE . Cara ini ditulis, $ USERSWHODIDNOTCHANGE tidak boleh berisi spasi. Jika memang membutuhkan spasi, di atas dapat ditulis ulang sebagai berikut:
if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
echo "no change: $USERSWHODIDNOTCHANGE"
fi
diff "File_1.txt" "File_2.txt"