Możemy użyć składni ${var##pattern}
i ${var%%pattern}
wyodrębnić ostatnią i pierwszą sekcję adresu 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##*.}
Jak możemy wyodrębnić drugą lub trzecią sekcję adresu IPv4 za pomocą rozszerzenia parametrów?
Oto moje rozwiązanie: używam tablicy i zmieniam zmienną 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
Również Pisałem kilka rozwiązań, za pomocą awk
, sed
i cut
poleceń.
Teraz moje pytanie brzmi: czy istnieje prostsze rozwiązanie oparte na rozszerzaniu parametrów, które nie wykorzystuje zmiany tablicy i IFS?
IFS
naread
tam:IFS=. read -a ArrIP<<<"$IP"
IFS=. read a b c d <<< "$IP"
jest nie do przyjęcia (jeśli używasz Bash, to znaczy)? Dlaczego trzeba to zrobić z rozszerzaniem parametrów?Odpowiedzi:
Zakładając domyślną wartość IFS, wyodrębniasz każdy oktet do własnej zmiennej za pomocą:
Lub do tablicy z:
źródło
Twoje zgłoszenie problemu może być nieco bardziej liberalne niż zamierzałeś. Ryzykując lukę, oto rozwiązanie, które nawiązywało do :
To jest trochę niezgrabne. Definiuje dwie zmienne usuwania i nie jest łatwo przystosowane do obsługi większej liczby sekcji (np. Dla adresu MAC lub IPv6). Odpowiedź Siergieja Kolodyazhnyya zainspirowała mnie do uogólnienia powyższego na to:
To określa
sec1
,sec2
,sec3
isec4
, które mogą być weryfikowanewhile
Pętli powinny być łatwe do zrozumienia - to iteracje czterokrotnie.slice
jako nazwę zmiennej, która zajmuje miejscelast3
ilast2
w moim pierwszym roztworze (powyżej).declare sec"$count"="value"
Jest to sposób przypisać dosec1
,sec2
,sec3
isec4
kiedycount
jest1
,2
,3
i4
. To trochę jakeval
, ale bezpieczniejsze.value
,"${slice%%.*}"
Jest analogiczna do wartości mojego pierwotnego przypisuje odpowiedź nafirst
,second
ithird
.źródło
Zdaję sobie sprawę, że konkretnie poprosiłeś o rozwiązanie, które NIE zostało tymczasowo na nowo zdefiniowane
IFS
, ale mam słodkie i proste rozwiązanie, którego nie omówiłeś, więc oto:Że krótka komenda będzie umieścić elementy adresu IP w powłoce za parametrów pozycyjnych
$1
,$2
,$3
,$4
. Najprawdopodobniej jednak będziesz chciał najpierw zapisać oryginał,IFS
a potem go przywrócić.Kto wie? Może ponownie rozważysz i zaakceptujesz tę odpowiedź ze względu na jej zwięzłość i skuteczność.
(To było wcześniej niepoprawnie podane jako
IFS=. set -- $IP
)źródło
IFS
w tym samym wierszu poleceń, nowa wartość nie zadziała, gdy zmienne w tym samym wierszu poleceń zostaną rozwinięte. Tak samo jak zx=1; x=2 echo $x
set
ogóle nie korzysta$IFS
,$IFS
jest używany tylko do dzielenia słów na$IP
, ale tutaj jest przypisany za późno, więc nie ma to żadnego efektu. Ta odpowiedź jest zasadniczo błędna.IP=109.96.77.15 bash -c 'IFS=. set -- $IP; echo "$2"'
nie wyświetla nic, czy jest w trybie POSIX, czy nie. PotrzebujeszIFS=. command eval 'set -- $IP'
, lubIFS=. read a b c d << "$IP"
.
jeden z poprzednich testów. Uruchom,IP=1.2.3.4 bash -xc 'IFS=. set $IP; echo "$2"'
a zobaczysz, że to nie działa. IIP=1.2.3.4 bash -o posix -xc 'IFS=. set $IP; echo "\$1=$1 \$2=$2 IFS=$IFS"'
zilustrujmy punkt @ chepnera.Nie najłatwiej , ale możesz zrobić coś takiego:
To powinno działać w ksh93 (jeżeli
${var//pattern/replacement}
operator pochodzi),bash
4.3+, BusyBoxsh
,yash
,mksh
izsh
, choć oczywiście wzsh
nie są o wiele prostsze sposoby . W starszych wersjachbash
trzeba by usunąć wewnętrzne cytaty. Działa z tymi wewnętrznymi cudzysłowami usuniętymi również w większości innych powłok, ale nie w ksh93.Zakłada się, że
$IP
zawiera prawidłową reprezentację adresu IPv4 w postaci dziesiętno-dziesiętnej (choć działałoby to również w przypadku reprezentacji cztero-szesnastkowych, takich jak0x6d.0x60.0x4d.0xf
(a nawet ósemkowa w niektórych powłokach), ale wyświetlałby wartości dziesiętnie). Jeśli zawartość$IP
pochodzi z niezaufanego źródła, oznacza to lukę w zabezpieczeniach polegającą na wstrzykiwaniu poleceń.Zasadniczo, jak jesteśmy zastępując każdy
.
w$IP
z+256*(
, skończymy oceny:Więc jesteśmy konstruowania 32 bitową liczbę całkowitą z tych 4 bajtów, takich jak adres IPv4 ostatecznie jest (choć z bajty odwrócone) ¹, a następnie przy użyciu
>>
,&
bitowe operatorów wyodrębnić odpowiednie bajty.Używamy
${param+value}
standardowego operatora (tutaj$-
zawsze gwarantuje się ustawienie) zamiast tego,value
ponieważ w przeciwnym razie parser arytmetyczny narzekałby na niedopasowany nawias. Powłoka tutaj może znaleźć zamknięcie))
dla otwarcia$((
, a następnie wykonać rozszerzenia wewnątrz, które spowodują wyrażenie arytmetyczne do oceny.Dzięki
zamiast, powłoka potraktuje drugi i trzeci$(((${IP//./"+256*("}))))&255))
)
s tam jako zamknięcie))
dla$((
i zgłosić błąd składni.W ksh93 możesz także:
bash
,mksh
,zsh
Zostały skopiowane ksh93 męska${var/pattern/replacement}
operatora, ale nie, że grupa przechwytywania obsługi część.zsh
obsługuje go z inną składnią:bash
obsługuje pewną formę obsługi grup przechwytywania w operatorze dopasowania wyrażeń regularnych , ale nie w${var/pattern/replacement}
.POSIXly użyłbyś:
noglob
Aby uniknąć złych niespodzianek dla wartości$IP
jak10.*.*.*
, podpowłoki aby ograniczyć zakres tych zmian do opcji i$IFS
.¹ Adres IPv4 to tylko 32-bitowa liczba całkowita, a na przykład 127.0.0.1 to tylko jedna z wielu (choć najczęstszych) reprezentacji tekstowych. Ten sam typowy adres IPv4 interfejsu sprzężenia zwrotnego można również przedstawić jako 0x7f000001 lub 127.1 (być może bardziej odpowiedni tutaj, aby powiedzieć, że jest to
1
adres w sieci klasy 127.0 / 8 klasy A) lub 0177.0.1, lub inne kombinacje 1 do 4 liczb wyrażonych jako liczba ósemkowa, dziesiętna lub szesnastkowa. Możesz przekazać je wszystkim,ping
na przykład, a zobaczysz, że wszystkie będą pingować localhost.Jeśli nie przeszkadza ci efekt uboczny ustawiania dowolnej zmiennej tymczasowej (tutaj
$n
), wbash
lubksh93
lubzsh -o octalzeroes
lublksh -o posix
, możesz po prostu przekonwertować wszystkie te reprezentacje z powrotem na 32-bitową liczbę całkowitą za pomocą:A następnie wyodrębnij wszystkie składniki za pomocą
>>
/&
kombinacji takich jak powyżej.mksh
używa 32-bitowych liczb całkowitych ze znakiem dla wyrażeń arytmetycznych, można$((# n=32,...))
tam użyć , aby wymusić użycie 32-bitowych liczb bez znaku (iposix
opcję rozpoznawania stałych ósemkowych).źródło
${-+
. Nie mogę też znaleźć żadnej dokumentacji na ten temat. Działa, ale jestem ciekawy, czy mogę zmienić ciąg w wyrażenie matematyczne? Gdzie mogę znaleźć formalną definicję? Ponadto dodatkowe cytaty w sekcji zastępowania rozszerzenia parametrów nie działają w GNU bash, wersja 4.1.2 (2) - wydanie CentOS 6.6. Musiałem to zrobić zamiast tegoecho "$((${-+"(${IP//./+256*(}))))"}>>16&255))"
Zsh umożliwia zagnieżdżanie podstawień parametrów:
Nie jest to możliwe w bash.
źródło
zsh
, może wolisz${${(s(.))ip}[3]}
Jasne, zagrajmy w grę słoni.
lub
źródło
Z
IP=12.34.56.78
.I
Opis:
Wykorzystanie rozszerzenia parametru
${IP// }
do przekształcenia każdej kropki w ip w nawias otwierający kropkę i nawias zamykający. Dodając nawias początkowy i nawias zamykający, otrzymujemy:który utworzy cztery nawiasy przechwytujące dla dopasowania wyrażenia regularnego w składni testowej:
To pozwala na wydrukowanie tablicy BASH_REMATCH bez pierwszego komponentu (całe dopasowanie wyrażenia regularnego):
Ilość nawiasów jest automatycznie dostosowywana do dopasowanego ciągu. Będzie to więc pasowało do adresu MAC lub EUI-64 adresu IPv6, mimo że mają różną długość:
Użyj tego:
źródło
Oto małe rozwiązanie wykonane przy pomocy POSIX
/bin/sh
(w moim przypadku todash
), funkcja, która wielokrotnie wykorzystuje rozszerzanie parametrów (więc nieIFS
tutaj), i nazwane potoki oraz zawieranoglob
opcję z powodów wymienionych w odpowiedzi Stephane'a .Działa to tak:
I
ip
zmieniono na109.*.*.*
Pętla utrzymująca licznik 4 iteracji odpowiada 4 sekcjom prawidłowego adresu IPv4, natomiast akrobatyka z nazwanymi potokami wymaga dalszego używania sekcji adresu IP w skrypcie, w przeciwieństwie do blokowania zmiennych w podpowłoce pętli.
źródło
Dlaczego nie skorzystać z prostego rozwiązania z awk?
$ IP="192.168.1.1" $ echo $IP | awk -F '.' '{ print $1" "$2" "$3" "$4;}'
Wynik
$ 192 168 1 1
źródło
źródło