우리는 구문을 사용할 수 있습니다 ${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
, 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
루프는 이해하기 쉬워야한다 – 그것은 네 번 반복 할.- 세르지는 선택
slice
의 대신하는 변수의 이름으로last3
와last2
내 첫 번째 솔루션 (위)에. declare sec"$count"="value"
에 할당하는 방법이다sec1
,sec2
,sec3
그리고sec4
때count
이다1
,2
,3
와4
. 조금 비슷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}
연산자에서 유래), bash
4.3, 비지 박스 sh
, yash
, mksh
과 zsh
, 물론 있지만 에서 zsh
, 훨씬 간단 방법이 있습니다 . 의 이전 버전에서는 bash
내부 따옴표를 제거해야합니다. ksh93이 아닌 대부분의 다른 쉘에서도 제거 된 내부 인용 부호와 함께 작동합니다.
$IP
IPv4 주소의 유효한 십진수 표현이 포함되어 있다고 가정 합니다 (그러나 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 ( 1
127.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 비트 숫자 ( posix
8 진 상수를 인식 하는 옵션) 를 강제로 사용할 수 있습니다 .
답변
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