배쉬 : IPv4 주소의 네 섹션 중 하나를 추출 the last section using ${var##pattern}:

우리는 구문을 사용할 수 있습니다 ${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 변경을 사용하지 않는 매개 변수 확장 을 기반으로 한 더 간단한 솔루션이 있습니까?



답변

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

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

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

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

답변

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

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그리고 sec4
    count이다 1, 2, 34. 조금 비슷 eval하지만 더 안전합니다.
  • value, "${slice%%.*}"는가 내 원래 응답 양수인 값에 유사 first, second하고 third.

답변

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

IFS=. ; set -- $IP

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

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

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


답변

가장 쉬운 것은 아니지만 다음과 같이 할 수 있습니다.

$ 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 진 상수를 인식 하는 옵션) 를 강제로 사용할 수 있습니다 .


답변

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

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

bash에서는 불가능합니다.


답변

물론 코끼리 게임을 해보자.

$ 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

답변

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