बैश: एक IPv4 पते के चार वर्गों में से एक को निकालें


9

हम सिंटैक्स का उपयोग कर सकते हैं ${var##pattern}और ${var%%pattern}एक 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##*.}

पैरामीटर विस्तार का उपयोग करके हम IPv4 पते के दूसरे या तीसरे खंड को कैसे निकाल सकते हैं?

यहाँ मेरा समाधान है: मैं एक सरणी का उपयोग करता हूं और 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

इसके अलावा मैं कुछ का उपयोग कर समाधान में लिखा है awk, sedऔर cutआदेशों।

अब, मेरा सवाल है: क्या पैरामीटर विस्तार के आधार पर एक सरल समाधान है जो सरणी और IFS को बदलने का उपयोग नहीं करता है?


1
आपको केवल वहां के IFSलिए सेट करना चाहिए read:IFS=. read -a ArrIP<<<"$IP"
muru

1
कम से कम कई वेरिएबल का उपयोग किए बिना बैश में नहीं। एक एकल पैरामीटर विस्तार को दूसरा या तीसरा घटक नहीं मिल सकता है। Zsh विस्तारक को घोंसला बना सकता है, इसलिए यह वहां संभव हो सकता है।
मुरु

@ एमआरयू क्या आप Zsh समाधान प्रदान कर सकते हैं?
Sci9

4
आपके पास क्या गारंटी है कि आप हमेशा IP v4 पते के साथ काम करेंगे, और कभी भी IP v6 पता नहीं होगा?
मावग का कहना है कि मोनिका

4
क्या कोई कारण IFS=. read a b c d <<< "$IP"स्वीकार्य नहीं है (यदि आप बैश का उपयोग कर रहे हैं, तो)? इसे पैरामीटर विस्तार के साथ क्यों करना पड़ता है?
ilkachachu

जवाबों:


16

IFS का डिफ़ॉल्ट मान मानकर आप प्रत्येक ऑक्टेट को इसके साथ स्वयं का चर निकालते हैं:

read A B C D <<<"${IP//./ }"

या इसके साथ एक सरणी में:

A=(${IP//./ })

2
+1। ऐसा लगता है कि यह सबसे सरल, सबसे सरल तरीका है जो ओपी प्रतिबंधों का पालन करता है।
डिजिटल ट्रामा

7

आपकी समस्या कथन आपके उद्देश्य से थोड़ी अधिक उदार हो सकती है। एक खामियों का फायदा उठाने के जोखिम में, यहाँ समाधान मुरू ने पीछा किया :

first=${IP%%.*}
last3=${IP#*.}
second=${last3%%.*}
last2=${last3#*.}
third=${last2%.*}
fourth=${last2#*.}
echo "$IP -> $first, $second, $third, $fourth"

यह कुछ हद तक क्लूनी है। यह दो फेंक-दूर चर को परिभाषित करता है, और यह अधिक वर्गों (जैसे मैक या आईपीवी 6 पते के लिए) को संभालने के लिए आसानी से अनुकूलित नहीं है।  सर्गी कोलोडियाज़नी के उत्तर ने मुझे इसके लिए सामान्यीकृत करने के लिए प्रेरित किया:

slice="$IP"
count=1
while [ "$count" -le 4 ]
do
    declare sec"$count"="${slice%%.*}"
    slice="${slice#*.}"
    count=$((count+1))
done

इस सेट sec1, sec2, sec3और sec4, साथ सत्यापित किया जा सकता है जो

printf 'Section 1: %s\n' "$sec1"
printf 'Section 2: %s\n' "$sec2"
printf 'Section 3: %s\n' "$sec3"
printf 'Section 4: %s\n' "$sec4"
  • whileपाश को समझने के लिए आसान होना चाहिए - यह चार बार iterates।
  • सर्गी चुना है sliceएक चर की जगह लेता है के लिए नाम के रूप में last3और last2(ऊपर) मेरी पहली समाधान में।
  • declare sec"$count"="value"करने के लिए आवंटित करने के लिए एक रास्ता है sec1, sec2, sec3और sec4 जब countहै 1, 2, 3और 4। यह थोड़ा पसंद है eval, लेकिन सुरक्षित है।
  • value, "${slice%%.*}", अनुरूप करने के लिए अपने मूल जवाब प्रदान करती है को महत्व देता है first, secondऔर third

6

मुझे लगता है कि आपने विशेष रूप से एक समाधान के लिए कहा है जो कि DID अस्थायी रूप से पुनर्परिभाषित नहीं करता है IFS, लेकिन मेरे पास एक मीठा और सरल उपाय है जिसे आपने कवर नहीं किया है, इसलिए यहां दिया गया है:

IFS=. ; set -- $IP

यही कारण है कि कम आदेश खोल के दशक में आपके आईपी पते के तत्वों डाल देंगे स्थितीय मापदंडों $1 , $2, $3, $4। हालाँकि, आप शायद पहले मूल को बचाना चाहते हैं IFSऔर बाद में इसे पुनर्स्थापित करना चाहते हैं ।

कौन जाने? हो सकता है कि आप पुनर्विचार करें और इस उत्तर को अपनी संक्षिप्तता और दक्षता के लिए स्वीकार करें।

(यह पहले गलत तरीके से दिया गया था IFS=. set -- $IP)


5
मुझे नहीं लगता कि यह काम करता है: यदि आप IFSएक ही कमांड लाइन पर बदलते हैं , तो नया मान प्रभावी नहीं होता है जब एक ही कमांड लाइन पर चर का विस्तार किया जाता है। उसी के साथx=1; x=2 echo $x
ilkachachu

6
@chepner, लेकिन फिर से, सभी setका उपयोग नहीं करता है $IFS, $IFSकेवल शब्द के विभाजन के लिए उपयोग किया जाता है $IP, लेकिन यहां इसे बहुत देर से सौंपा गया है, ताकि इसका कोई प्रभाव न हो। यह उत्तर मूल रूप से गलत है। IP=109.96.77.15 bash -c 'IFS=. set -- $IP; echo "$2"'POSIX मोड में कुछ भी आउटपुट नहीं है या नहीं। आप आवश्यकता होगी IFS=. command eval 'set -- $IP', याIFS=. read a b c d << "$IP"
स्टीफन Chazelas

4
यह शायद आपके लिए काम करता है क्योंकि आपने .अपने पिछले परीक्षणों में से एक में आईएफएस सेट किया था । दौड़ें IP=1.2.3.4 bash -xc 'IFS=. set $IP; echo "$2"'और आप देखेंगे कि यह काम नहीं करता है। और IP=1.2.3.4 bash -o posix -xc 'IFS=. set $IP; echo "\$1=$1 \$2=$2 IFS=$IFS"'@ chepner की बात को स्पष्ट करने के लिए देखें ।
स्टीफन चेज़लस

2
जल्दबाजी ने बेकार कर दिया, जवाब स्पष्ट रूप से गलत है, और काम करने के लिए सही किया जाना चाहिए, क्योंकि यह अभी भी मोटे तौर पर है कि मैं इसे कैसे करूंगा, केवल अधिक कोड के साथ।
छिपकली

3
@ user1404316, क्या आप उन आदेशों का सटीक सेट पोस्ट कर सकते हैं, जिन्हें आपने इसका परीक्षण किया था? (प्लस आपके शेल का संस्करण, शायद।) चार अन्य उपयोगकर्ता हैं जिन्होंने आपको यहां टिप्पणियों में बताया है कि यह उत्तर में लिखे अनुसार काम नहीं करता है। उदाहरण के साथ।
इलकाचू

5

सबसे आसान नहीं है , लेकिन आप कुछ ऐसा कर सकते हैं:

$ IP=109.96.77.15
$ echo "$((${-+"(${IP//./"+256*("}))))"}&255))"
109
$ echo "$((${-+"(${IP//./"+256*("}))))"}>>8&255))"
96
$ echo "$((${-+"(${IP//./"+256*("}))))"}>>16&255))"
77
$ echo "$((${-+"(${IP//./"+256*("}))))"}>>24&255))"
15

यही कारण है कि ksh93 में काम करना चाहिए (जहां कि ${var//pattern/replacement}ऑपरेटर से आता है), bash4.3+, बिजीबॉक्स sh, yash, mkshऔर zsh, हालांकि निश्चित रूप से में zsh, वहाँ बहुत सरल दृष्टिकोण हैं । पुराने संस्करणों में bash, आपको आंतरिक उद्धरण निकालने होंगे। यह उन आंतरिक उद्धरणों के साथ काम करता है जो अधिकांश अन्य गोले में भी हटाए जाते हैं, लेकिन ksh93 नहीं।

उस मान $IPमें एक IPv4 पते का एक मान्य क्वाड-दशमलव प्रतिनिधित्व होता है (हालांकि यह क्वाड-हेक्साडेसिमल निरूपण के लिए भी काम करेगा 0x6d.0x60.0x4d.0xf(और कुछ गोले में भी अष्टक) लेकिन दशमलव में मूल्यों का उत्पादन करेगा)। यदि सामग्री $IPएक अविश्वसनीय स्रोत से आती है, तो यह एक कमांड इंजेक्शन भेद्यता की राशि होगी।

मूल रूप से, के रूप में हम हर बदल रहे हैं .में $IPसाथ +256*(, हम मूल्यांकन कर अंत:

 $(( (109+256*(96+256*(77+256*(15))))>> x &255 ))

इसलिए हम एक IPv4 पते की तरह उन 4 बाइट के बाहर एक 32 बिट पूर्णांक के निर्माण कर रहे हैं अंत में है (बाइट्स हालांकि साथ उलट) ¹ में और उसके बाद का उपयोग कर >>, &बिटवाइज़ ऑपरेटर्स प्रासंगिक बाइट्स को निकालने के लिए।

हम ${param+value}मानक ऑपरेटर का उपयोग करते हैं ( $-जिस पर हमेशा सेट होने की गारंटी दी जाती है) सिर्फ valueइसलिए कि अन्यथा अंकगणित पार्सर बेमेल कोष्ठक के बारे में शिकायत करेगा। यहां खोल ))उद्घाटन के लिए समापन का पता लगा सकता है $((, और फिर इसके अंदर विस्तार का प्रदर्शन कर सकता है जिसके परिणामस्वरूप अंकगणितीय अभिव्यक्ति का मूल्यांकन किया जाएगा।

साथ $(((${IP//./"+256*("}))))&255))बजाय, खोल दूसरे और तीसरे का इलाज होगा )समापन के रूप में वहाँ रों ))के लिए $((और एक सिंटैक्स त्रुटि रिपोर्ट।

Ksh93 में, आप यह भी कर सकते हैं:

$ echo "${IP/@(*).@(*).@(*).@(*)/\2}"
96

bash, ksh93 के ऑपरेटर को कॉपी किया है mksh, लेकिन उस कैप्चर-ग्रुप हैंडलिंग पार्ट को नहीं। एक अलग सिंटैक्स के साथ इसका समर्थन करता है:zsh${var/pattern/replacement}zsh

$ setopt extendedglob # for (#b)
$ echo ${IP/(#b)(*).(*).(*).(*)/$match[2]}'
96

bashअपने regexp मिलान ऑपरेटर में कैप्चर ग्रुप हैंडलिंग के कुछ प्रकार का समर्थन करता है , लेकिन अंदर नहीं ${var/pattern/replacement}

POSIXly, आप उपयोग करेंगे:

(IFS=.; set -o noglob; set -- $IP; printf '%s\n' "$2")

noglobके मूल्यों के लिए बुरा आश्चर्य से बचने के लिए $IPकी तरह 10.*.*.*subshell, विकल्प और करने के लिए उन परिवर्तनों का दायरा सीमित करने के लिए $IFS


Inte एक आईपीवी 4 पता सिर्फ 32 बिट पूर्णांक है और उदाहरण के लिए 127.0.0.1 कई (हालांकि सबसे आम) पाठीय अभ्यावेदन में से एक है। लूपबैक इंटरफ़ेस का वही विशिष्ट IPv4 पता 0x7f000001 या 127.1 के रूप में भी प्रस्तुत किया जा सकता है (शायद यह कहने के लिए कि यह 1127.0 / 8 वर्ग A नेटवर्क पर पता लगाने के लिए अधिक उपयुक्त है ), या 0177.0.1, या 1 के अन्य संयोजन 4 संख्याओं को अष्टाधारी, दशमलव या षोडश आधारी के रूप में व्यक्त किया जाता है। आप pingउदाहरण के लिए उन सभी को पास कर सकते हैं और आप देखेंगे कि वे सभी लोकलहोस्ट पिंग करेंगे।

आप (यहाँ एक मनमाना अस्थायी चर की स्थापना के पक्ष प्रभाव कोई आपत्ति नहीं है $n), में bashया ksh93या zsh -o octalzeroesया lksh -o posix, तो आप बस के साथ एक 32 बिट पूर्णांक पर वापस उन सभी अभ्यावेदन में बदल सकते हैं:

$((n=32,(${IP//./"<<(n-=8))+("})))

और फिर ऊपर >>/ &संयोजन के साथ सभी घटकों को निकालें ।

$ IP=0x7f000001
$ echo "$((n=32,(${IP//./"<<(n-=8))+("})))"
2130706433
$ IP=127.1
$ echo "$((n=32,(${IP//./"<<(n-=8))+("})))"
2130706433
$ echo "$((n=32,((${IP//./"<<(n-=8))+("}))>>24&255))"
127
$ perl -MSocket -le 'print unpack("L>", inet_aton("127.0.0.1"))'
2130706433

mkshअपने अंकगणितीय अभिव्यक्तियों के लिए हस्ताक्षरित 32 बिट पूर्णांक का उपयोग करता है, आप $((# n=32,...))वहां उपयोग कर सकते हैं अहस्ताक्षरित 32 बिट संख्या (और posixऑक्टल स्थिरांक को पहचानने के लिए विकल्प) का उपयोग करने के लिए।


मैं बड़ी अवधारणा को समझता हूं, लेकिन मैंने पहले कभी नहीं देखा ${-+। मैं इस पर कोई भी दस्तावेज नहीं ढूँढ सकता। यह काम करता है, लेकिन मैं सिर्फ पुष्टि करने के लिए उत्सुक हूं, क्या यह स्ट्रिंग को गणितीय अभिव्यक्ति में बदलना है? मुझे औपचारिक परिभाषा कहां मिल सकती है? इसके अलावा, पैरामीटर विस्तार प्रतिस्थापन अनुभाग के अंदर अतिरिक्त कोटेशन GNU बैश, संस्करण 4.1.2 (2) -release CentOS 6.6 में काम नहीं करते हैं। मुझे इसके बजाय यह करना थाecho "$((${-+"(${IP//./+256*(}))))"}>>16&255))"
लेवी उज़ोडिके

1
@LeviUzodike, देखें संपादित करें।
स्टीफन चेजालस

4

Zsh के साथ, आप पैरामीटर प्रतिस्थापन को घोंसला कर सकते हैं:

$ ip=12.34.56.78
$ echo ${${ip%.*}##*.}
56
$ echo ${${ip#*.}%%.*}
34

यह मारपीट में संभव नहीं है।


1
में zsh, आप पसंद कर सकते हैं${${(s(.))ip}[3]}
स्टीफन चेजेलस

4

ज़रूर, चलो हाथी खेल खेलते हैं।

$ ipsplit() { local IFS=.; ip=(.$*); }
$ ipsplit 10.1.2.3
$ echo ${ip[1]}
10

या

$ ipsplit() { local IFS=.; echo $*; }
$ set -- `ipsplit 10.1.2.3`
$ echo $1
10

2
"हाथी का खेल" क्या है?
वाइल्डकार्ड

1
@Wildcard एक अण्डाकार दंड / मजाक की तरह, यह अंधे लोगों के लिए एक संदर्भ है-एक हाथी की कहानी का वर्णन करने वाले दोनों, वहाँ हर किसी को अपने स्वयं के लेने के लिए, और एक राजा के प्राचीन रीति-रिवाज को देखने के लिए कुछ हिस्सों की आवश्यकता हो सकती है। महंगे महंगे रखरखाव के साथ उपहार देने के लिए, महज संदिग्ध मूल्य के उपहारों के लिए सदियों से नरम बने हुए हैं जो मनोरंजन मूल्य के लिए फिर से संगठित हो रहे हैं .. दोनों को यहां आवेदन करना प्रतीत होता है :-)
jthill

3

के साथ IP=12.34.56.78

IFS=. read a b c d <<<"$IP"

तथा

#!/bin/bash
IP=$1

regex="(${IP//\./)\.(})"
[[ $IP =~ $regex ]]
echo "${BASH_REMATCH[@]:1}"

विवरण:

${IP// }आईपी ​​को खोलने वाले कोष्ठक को एक बिंदु और समापन कोष्ठक को बदलने के लिए पैरामीटर विस्तार का उपयोग । प्रारंभिक कोष्ठक और समापन कोष्ठक जोड़ना, हमें मिलता है:

regex=(12)\.(34)\.(56)\.(78)

टेस्ट सिंटैक्स में रेगेक्स मैच के लिए चार कैप्चर कोष्ठक बनाएंगे:

[[ $IP =~ $regex ]]

यह पहले घटक (पूरे रेगेक्स मैच) के बिना सरणी BASH_REMATCH के मुद्रण की अनुमति देता है:

echo "${BASH_REMATCH[@]:1}"

कोष्ठक की मात्रा स्वचालित रूप से मिलान स्ट्रिंग से समायोजित की जाती है। इसलिए, यह अलग लंबाई के होने के बावजूद IPv6 पते के MAC या EUI-64 से मेल खाएगा:

#!/bin/bash
IP=$1

regex="(${IP//:/):(})"
[[ $IP =~ $regex ]]
echo "${BASH_REMATCH[@]:1}"

उसका इस्तेमाल कर रहे हैं:

$ ./script 00:0C:29:0C:47:D5
00 0C 29 0C 47 D5

$ ./script 00:0C:29:FF:FE:0C:47:D5
00 0C 29 FF FE 0C 47 D5

3

यहां POSIX के साथ किया गया एक छोटा सा समाधान है /bin/sh(मेरे मामले में dash), एक फ़ंक्शन जो बार-बार पैरामीटर विस्तार (इसलिए IFSयहां नहीं ) और नामित पाइप का उपयोग करता है , और स्टीफन के उत्तरnoglob में वर्णित कारणों के लिए विकल्प भी शामिल है ।

#!/bin/sh
set -o noglob
get_ip_sections(){
    slice="$1"
    count=0
    while [ -n "${slice}" ] && [ "$count" -ne 4 ]
    do
        num="${slice%%.*}"
        printf '%s ' "${num}"
        slice="${slice#*${num}.}"
        count=$((count+1))
    done
}

ip="109.96.77.15"
named_pipe="/tmp/ip_stuff.fifo"
mkfifo "${named_pipe}"
get_ip_sections "$ip" > "${named_pipe}" &
read sec1 sec2 sec3 sec4 < "${named_pipe}"
printf 'Actual ip:%s\n' "${ip}"
printf 'Section 1:%s\n' "${sec1}"
printf 'Section 3:%s\n' "${sec3}"
rm  "${named_pipe}"

यह इस तरह काम करता है:

$ ./get_ip_sections.sh 
Actual ip:109.96.77.15
Section 1:109
Section 3:77

और के साथ ipबदल दिया है109.*.*.*

$ ./get_ip_sections.sh 
Actual ip:109.*.*.*
Section 1:109
Section 3:*

4 पुनरावृत्तियों का काउंटर रखने के लिए मान्य आईपीवी 4 पते के 4 वर्गों के लिए खाते हैं, जबकि नाम के पाइप के साथ कलाबाजी के लिए स्क्रिप्ट के भीतर आईपी पते के अनुभागों का उपयोग करने की आवश्यकता होती है, क्योंकि लूप के उप-भाग में अटके हुए चर के विपरीत।


मैंने आपके उत्तर को संशोधित कर दिया है, इसलिए यह पाइप का उपयोग नहीं करता है और इसे मेरे मौजूदा उत्तर में जोड़ दिया है
जी-मैन ने कहा कि 'मोनिका' बहाल

0

जाग के साथ सरल समाधान का उपयोग क्यों न करें?

$ IP="192.168.1.1" $ echo $IP | awk -F '.' '{ print $1" "$2" "$3" "$4;}'

परिणाम $ 192 168 1 1


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.