배쉬 : 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
적어도 여러 변수를 사용하지 않고 bash에 없습니다. 단일 매개 변수 확장으로 두 번째 또는 세 번째 구성 요소를 얻을 수 없습니다. Zsh는 매개 변수 확장을 중첩 할 수 있으므로 가능할 수도 있습니다.
muru

@muru Zsh 솔루션을 제공해 주시겠습니까?
sci9

4
항상 IP v4 주소를 처리하고 IP v6 주소를 가지지 않는다는 보장은 무엇입니까?
Mawg는 모니카 복원

4
IFS=. read a b c d <<< "$IP"허용되지 않는 이유 가 있습니까 (Bash를 사용하는 경우)? 매개 변수 확장을 사용해야하는 이유는 무엇입니까?
ilkkachu

답변:


16

IFS의 기본값을 가정하면 다음을 사용하여 각 옥텟을 자체 변수로 추출합니다.

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

또는 다음과 같은 배열로 배열하십시오.

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

2
+1. 이것이 OPs 제한을 준수하는 가장 단순하고 가장 간단한 방법 인 것 같습니다.
디지털 외상

7

문제 진술은 의도 한 것보다 조금 더 자유로울 수 있습니다. 허점을 악용 할 위험이있는 솔루션 다음과 같습니다.

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

이것은 다소 어수선합니다. 이 변수는 두 가지 폐기 변수를 정의하며 더 많은 섹션 (예 : MAC 또는 IPv6 주소)을 처리하기에 적합하지 않습니다.  Sergiy Kolodyazhnyy의 답변 은 위의 내용을 다음과 같이 일반화했습니다.

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

이 세트는 sec1, sec2, sec3sec4, 확인 할 수있는

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루프는 이해하기 쉬워야한다 - 그것은 네 번 반복 할.
  • 세르지는 선택 slice의 대신하는 변수의 이름으로 last3last2내 첫 번째 솔루션 (위)에.
  • declare sec"$count"="value"에 할당하는 방법이다 sec1, sec2, sec3그리고 sec4count이다 1, 2, 34. 조금 비슷 eval하지만 더 안전합니다.
  • value, "${slice%%.*}"는가 내 원래 응답 양수인 값에 유사 first, second하고 third.

6

나는 당신이 일시적으로 재정의하지 않은 해결책을 구체적으로 요구 IFS했지만, 당신이 다루지 않은 달콤하고 간단한 해결책을 가지고 있다는 것을 알고 있습니다.

IFS=. ; set -- $IP

그 짧은 명령은 쉘에 IP 주소의 요소를 넣어 위치 매개 변수 $1 , $2, $3, $4. 그러나 원본을 먼저 저장하고 IFS나중에 복원하는 것이 좋습니다.

누가 알아? 어쩌면 당신은 간결하고 효율적 으로이 답변을 재고하고 받아 들일 것입니다.

(이것은 이전에 잘못 잘못되었습니다 IFS=. set -- $IP)


5
나는 그것이 효과가 있다고 생각하지 않습니다 : IFS동일한 명령 줄에서 변경 하면 동일한 명령 줄의 변수가 확장 될 때 새 값이 적용되지 않습니다. 와 동일x=1; x=2 echo $x
ilkkachu

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"
Stéphane Chazelas

4
.이전 테스트 중 하나에서 IFS를 설정했기 때문에 아마도 효과가있을 것입니다 . 실행 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의 요점을 설명하기 위해 참조하십시오 .
Stéphane Chazelas

2
서두르면 낭비가 발생하고 그 대답은 분명히 틀 렸으며 여전히 더 많은 코드를 사용하여 대략 어떻게해야하기 때문에 작동하도록 수정해야합니다.
Lizardx

3
@ user1404316, 테스트에 사용한 정확한 명령 집합을 게시 할 수 있습니까? (아마도 쉘 버전 일 것입니다.) 여기에 답변에 쓰여진대로 작동하지 않는다는 의견을 말한 다른 사용자가 네 명 있습니다. 예를 들어.
ilkkachu

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, mkshzsh, 물론 있지만 에서 zsh, 훨씬 간단 방법이 있습니다 . 의 이전 버전에서는 bash내부 따옴표를 제거해야합니다. ksh93이 아닌 대부분의 다른 쉘에서도 제거 된 내부 인용 부호와 함께 작동합니다.

$IPIPv4 주소의 유효한 십진수 표현이 포함되어 있다고 가정 합니다 (그러나 0x6d.0x60.0x4d.0xf일부 쉘의 경우 8 진수와 같은 16 진수 표현에도 작동 하지만 값을 10 진수로 출력 함). 내용이 $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, mksh, zsh 의 경우 ksh93의 복사 한 ${var/pattern/replacement}연산자를하지만 캡처 그룹은 부분을 처리하지 않는 것이. zsh다른 구문으로 지원합니다.

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

bash정규 표현식 일치 연산자 에서는 캡처 그룹 처리의 형식을 지원하지만 지원 하지는 않습니다 ${var/pattern/replacement}.

POSIXly, 당신은 다음을 사용합니다 :

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

noglob값 나쁜 놀라움을 피하기 위해 $IP같은 10.*.*.*서브 쉘이 옵션에 이러한 변경 사항의 범위를 제한하는, $IFS.


¹ IPv4 주소는 32 비트 정수이고 127.0.0.1은 가장 일반적인 텍스트 표현 중 하나 일뿐입니다. 루프백 인터페이스의 동일한 일반적인 IPv4 주소는 0x7f000001 또는 127.1 ( 1127.0 / 8 클래스 A 네트워크 의 주소 라고 말하기에 더 적합 할 수도 있음 ) 또는 0177.0.1 또는 1의 다른 조합으로 표시 될 수 있습니다. 8 진수, 10 진수 또는 16 진수로 표현 된 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 비트 숫자 ( posix8 진 상수를 인식 하는 옵션) 를 강제로 사용할 수 있습니다 .


나는 큰 개념을 이해하지만 전에는 본 적이 없다 ${-+. 나는 그것에 대한 문서를 찾을 수 없습니다. 작동하지만 확인하기 만하면 궁금합니다. 문자열을 수학 표현식으로 바꾸는 것입니까? 공식적인 정의는 어디에서 찾을 수 있습니까? 또한 매개 변수 확장 대체 섹션의 추가 인용문은 GNU bash 버전 4.1.2 (2)-릴리즈 CentOS 6.6에서 작동하지 않습니다. 나는 이것을 대신해야했다echo "$((${-+"(${IP//./+256*(}))))"}>>16&255))"
Levi Uzodike

1
@LeviUzodike, 편집을 참조하십시오.
Stéphane Chazelas

4

zsh를 사용하면 매개 변수 대체를 중첩 할 수 있습니다.

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

bash에서는 불가능합니다.


1
에서 zsh, 당신은 선호 할 것입니다${${(s(.))ip}[3]}
Stéphane Chazelas

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 종류의 타원형 말장난 / 농담, 그것은 맹인을 묘사하는 코끼리 이야기를 언급하는 것입니다. 엔터테인먼트 값 regifted되는 경향이 단순히 반신 반의하는 가치의 선물 .. 모두에 수세기 동안 부드럽게 ruinously 비싼 유지비와 선물을 제공하는 것은 :-) 여기에 적용 할 듯
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// }ip의 각 점을 여는 괄호로 바꾸고 매개 변수 확장 을 사용 하여 닫는 괄호 초기 괄호와 닫는 괄호를 추가하면 다음과 같은 이점이 있습니다.

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

테스트 구문에서 정규식 일치에 대한 네 개의 캡처 괄호를 만듭니다.

[[ $IP =~ $regex ]]

이를 통해 첫 번째 구성 요소 (전체 정규식 일치)없이 배열 BASH_REMATCH를 인쇄 할 수 있습니다.

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

괄호의 양은 일치하는 문자열에 자동으로 조정됩니다. 따라서 길이가 다르더라도 MAC 또는 IPv6 주소의 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및 명명 된 파이프 및 Stephane의 답변에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 반복의 루프 유지 카운터는 유효한 IPv4 주소의 4 섹션을 차지하는 반면, 명명 된 파이프를 사용하는 곡예는 루프의 서브 쉘에 변수가 붙어있는 대신 스크립트 내에서 ip 주소의 섹션을 추가로 사용해야합니다.


파이프를 사용하지 않고 기존 답변에 추가하도록 답변을 수정했습니다 .
G-남자 '는 분석 재개 모니카'말한다

0

왜 awk와 함께 간단한 솔루션을 사용하지 않습니까?

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

결과 $ 192 168 1 1


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.