Kita dapat menggunakan sintaks ${var##pattern}
dan ${var%%pattern}
untuk mengekstrak bagian terakhir dan pertama dari alamat IPv4:
IP=109.96.77.15
echo IP: $IP
echo 'Extract the first section using ${var%%pattern}: ' ${IP%%.*}
echo 'Extract the last section using ${var##pattern}: ' ${IP##*.}
Bagaimana kita dapat mengekstrak bagian kedua atau ketiga dari alamat IPv4 menggunakan ekspansi parameter?
Ini solusi saya: Saya menggunakan array dan mengubah variabel IFS.
:~/bin$ IP=109.96.77.15
:~/bin$ IFS=. read -a ArrIP<<<"$IP"
:~/bin$ echo ${ArrIP[1]}
96
:~/bin$ printf "%s\n" "${ArrIP[@]}"
109
96
77
15
Juga saya telah menulis beberapa solusi menggunakan awk
, sed
dan cut
perintah.
Sekarang, pertanyaan saya adalah: Apakah ada solusi yang lebih sederhana berdasarkan ekspansi parameter yang tidak menggunakan perubahan array dan IFS?
IFS
untuk diread
sana:IFS=. read -a ArrIP<<<"$IP"
IFS=. read a b c d <<< "$IP"
yang tidak dapat diterima (jika Anda menggunakan Bash, itu)? Mengapa harus dilakukan dengan ekspansi parameter?Jawaban:
Dengan asumsi nilai default IFS Anda mengekstrak setiap oktet ke dalam variabel itu sendiri dengan:
Atau ke dalam array dengan:
sumber
Pernyataan masalah Anda mungkin sedikit lebih liberal daripada yang Anda maksudkan. Dengan risiko mengeksploitasi celah, inilah solusi yang disinggung oleh muru :
Ini agak kikuk. Ini mendefinisikan dua variabel yang dibuang, dan tidak mudah disesuaikan untuk menangani lebih banyak bagian (misalnya, untuk alamat MAC atau IPv6). Jawaban Sergiy Kolodyazhnyy menginspirasi saya untuk menggeneralisasi hal di atas sebagai berikut:
Set ini
sec1
,sec2
,sec3
dansec4
, yang dapat diverifikasi denganwhile
loop harus mudah dipahami - iterates empat kali.slice
sebagai nama untuk variabel yang menggantikanlast3
danlast2
dalam solusi pertama saya (di atas).declare sec"$count"="value"
adalah cara untuk menetapkan kesec1
,sec2
,sec3
dansec4
saatcount
ini1
,2
,3
dan4
. Agak miripeval
, tapi lebih aman.value
,,"${slice%%.*}"
analog dengan nilai yang diberikan oleh jawaban asli sayafirst
,second
danthird
.sumber
Saya menyadari bahwa Anda secara khusus meminta solusi yang TIDAK DITENTUKAN sementara
IFS
, tetapi saya punya solusi manis dan sederhana yang tidak Anda bahas, jadi begini:Bahwa perintah singkat akan menempatkan unsur-unsur alamat IP Anda di shell parameter posisi
$1
,$2
,$3
,$4
. Namun, Anda mungkin ingin menyimpan yang asliIFS
dan mengembalikannya setelah itu.Siapa tahu? Mungkin Anda akan mempertimbangkan kembali dan menerima jawaban ini karena singkat dan efisiensinya.
(Ini sebelumnya salah diberikan sebagai
IFS=. set -- $IP
)sumber
IFS
pada baris perintah yang sama, nilai baru tidak berpengaruh ketika variabel pada baris perintah yang sama diperluas. Sama seperti denganx=1; x=2 echo $x
set
tidak menggunakan$IFS
sama sekali,$IFS
hanya digunakan untuk pemisahan kata$IP
, tapi di sini sudah terlambat, sehingga tidak ada efeknya. Jawaban ini pada dasarnya salah.IP=109.96.77.15 bash -c 'IFS=. set -- $IP; echo "$2"'
tidak menghasilkan apa-apa dalam mode POSIX atau tidak. Anda perluIFS=. command eval 'set -- $IP'
, atauIFS=. read a b c d << "$IP"
.
di salah satu tes sebelumnya. JalankanIP=1.2.3.4 bash -xc 'IFS=. set $IP; echo "$2"'
dan Anda akan melihatnya tidak bekerja. Dan lihatIP=1.2.3.4 bash -o posix -xc 'IFS=. set $IP; echo "\$1=$1 \$2=$2 IFS=$IFS"'
untuk mengilustrasikan poin @ chepner.Bukan yang termudah , tetapi Anda dapat melakukan sesuatu seperti:
Yang harus bekerja di ksh93 (mana yang
${var//pattern/replacement}
Operator berasal dari),bash
4.3+, busyboxsh
,yash
,mksh
danzsh
, meskipun tentu saja dizsh
, ada pendekatan lebih sederhana . Di versi yang lebih lamabash
, Anda harus menghapus kutipan dalam. Ini bekerja dengan kutipan batin yang dihapus di sebagian besar kerang lain juga, tetapi tidak ksh93.Asumsi itu
$IP
berisi representasi quad-desimal valid dari alamat IPv4 (meskipun itu juga akan berfungsi untuk representasi quad-hexadecimal seperti0x6d.0x60.0x4d.0xf
(dan bahkan oktal dalam beberapa shell) tetapi akan menampilkan nilai dalam desimal). Jika konten$IP
berasal dari sumber yang tidak dipercaya, itu akan berarti kerentanan injeksi perintah.Pada dasarnya, seperti yang kita mengganti setiap
.
di$IP
dengan+256*(
, kita akhirnya mengevaluasi:Jadi kita membangun integer 32 bit dari 4 byte seperti alamat IPv4 pada akhirnya adalah (meskipun dengan byte terbalik)) dan kemudian menggunakan
>>
,&
operator bitwise untuk mengekstrak byte yang relevan.Kami menggunakan
${param+value}
operator standar (di sini$-
yang dijamin akan selalu ditetapkan) alih-alih hanyavalue
karena parser aritmatika akan mengeluh tentang kurung yang tidak cocok. Shell di sini dapat menemukan penutup))
untuk pembukaan$((
, dan kemudian melakukan ekspansi di dalam yang akan menghasilkan ekspresi aritmatika untuk dievaluasi.Dengan
gantinya, shell akan memperlakukan kedua dan ketiga$(((${IP//./"+256*("}))))&255))
)
s ada sebagai penutup))
untuk$((
dan melaporkan kesalahan sintaks.Di ksh93, Anda juga dapat melakukan:
bash
,mksh
,zsh
Telah menyalin ksh93 ini${var/pattern/replacement}
Operator tapi tidak yang menangkap-kelompok penanganan bagian.zsh
mendukungnya dengan sintaks yang berbeda:bash
mendukung beberapa bentuk penanganan grup tangkap di operator pencocokan regexp , tetapi tidak dalam${var/pattern/replacement}
.POSIXly, Anda akan menggunakan:
Untuk
noglob
menghindari kejutan buruk untuk nilai$IP
suka10.*.*.*
, subkulit untuk membatasi ruang lingkup perubahan tersebut ke opsi dan$IFS
.¹ Alamat IPv4 hanya integer 32 bit dan 127.0.0.1 misalnya hanyalah salah satu dari banyak (meskipun yang paling umum) representasi tekstual. Alamat IPv4 tipikal yang sama dari antarmuka loopback juga dapat direpresentasikan sebagai 0x7f000001 atau 127.1 (mungkin yang lebih tepat di sini adalah
1
alamat pada jaringan 127.0 / 8 kelas A), atau 0177.0.1, atau kombinasi lain dari 1 ke 4 angka yang dinyatakan sebagai oktal, desimal atau heksadesimal. Anda dapat mengirimkan semua ituping
misalnya dan Anda akan melihat mereka semua melakukan ping localhost.Jika Anda tidak keberatan efek samping menetapkan variabel sementara sewenang-wenang (di sini
$n
), dibash
atauksh93
atauzsh -o octalzeroes
ataulksh -o posix
, Anda dapat mengkonversi semua representasi mereka kembali ke integer 32 bit dengan:Lalu ekstrak semua komponen dengan
>>
/&
kombinasi seperti di atas.mksh
menggunakan bilangan bulat 32 bit yang ditandatangani untuk ekspresi aritmatika, Anda dapat menggunakannya di$((# n=32,...))
sana untuk memaksa penggunaan angka 32 bit yang tidak ditandatangani (danposix
opsi untuknya mengenali konstanta oktal).sumber
${-+
sebelumnya. Saya juga tidak dapat menemukan dokumentasi di situ. Berhasil, tapi saya hanya ingin memastikan, apakah hanya untuk mengubah string menjadi ekspresi matematika? Di mana saya dapat menemukan definisi formal? Juga, kutipan tambahan di dalam bagian penggantian ekspansi parameter tidak berfungsi di GNU bash, versi 4.1.2 (2) - rilis CentOS 6.6. Saya harus melakukan ini sebagai gantinyaecho "$((${-+"(${IP//./+256*(}))))"}>>16&255))"
Dengan zsh, Anda dapat membuat pengganti parameter:
Ini tidak mungkin dalam bash.
sumber
zsh
, Anda dapat memilih${${(s(.))ip}[3]}
Tentu, mari kita mainkan permainan gajah.
atau
sumber
Dengan
IP=12.34.56.78
.Dan
Deskripsi:
Gunakan ekspansi parameter
${IP// }
untuk mengonversi setiap titik di ip ke tanda kurung buka tanda kurung dan tanda kurung penutup. Menambahkan tanda kurung awal dan tanda kurung penutup, kita mendapatkan:yang akan membuat empat kurung tangkap untuk kecocokan regex dalam sintaks tes:
Itu memungkinkan pencetakan array BASH_REMATCH tanpa komponen pertama (kecocokan seluruh regex):
Jumlah tanda kurung secara otomatis disesuaikan dengan string yang cocok. Jadi, ini akan cocok dengan MAC atau EUI-64 dari alamat IPv6 meskipun panjangnya berbeda:
Menggunakannya:
sumber
Berikut adalah solusi kecil yang dilakukan dengan POSIX
/bin/sh
(dalam kasus saya itudash
), sebuah fungsi yang berulang kali menggunakan ekspansi parameter (jadi tidak ada diIFS
sini), dan menamai pipa, dan menyertakannoglob
opsi untuk alasan yang disebutkan dalam jawaban Stephane .Ini berfungsi seperti itu:
Dan dengan
ip
berubah menjadi109.*.*.*
Penghitung loop dari 4 iterasi menyumbang 4 bagian dari alamat IPv4 yang valid, sementara akrobat dengan akun pipa bernama perlu untuk menggunakan bagian ip address lebih lanjut dalam skrip sebagai lawan memiliki variabel yang terjebak dalam subshell dari loop.
sumber
Mengapa tidak menggunakan solusi sederhana dengan awk?
$ IP="192.168.1.1" $ echo $IP | awk -F '.' '{ print $1" "$2" "$3" "$4;}'
Hasil
$ 192 168 1 1
sumber
sumber