web-dev-qa-db-ja.com

未使用のローカルポートを見つける最も簡単な方法は何ですか?

未使用のローカルポートを見つける最も簡単な方法は何ですか?

現在、私はこれに似たものを使用しています:

port=$RANDOM
quit=0

while [ "$quit" -ne 1 ]; do
  netstat -a | grep $port >> /dev/null
  if [ $? -gt 0 ]; then
    quit=1
  else
    port=`expr $port + 1`
  fi
done

それはひどく回り道をしているので、私が見逃したビルトインのようなもっと単純なパスがあるのだろうかと思います。

58
mybuddymichael

アプリケーションでサポートされている場合は、ポート0をアプリケーションに渡してみてください。アプリケーションがこれをカーネルに渡すと、ポートは要求時に動的に割り当てられ、使用されないことが保証されます(すべてのポートがすでに使用されている場合、割り当ては失敗します)。

それ以外の場合は、これを手動で実行できます。あなたの回答のスクリプトには競合状態があり、それを回避する唯一の方法は、それを開こうとすることによってそれが開いているかどうかを原子的にチェックすることです。ポートが使用中の場合、プログラムはポートを開くことに失敗して終了するはずです。

たとえば、GNU netcat。

#!/bin/bash
read lower_port upper_port < /proc/sys/net/ipv4/ip_local_port_range
while :; do
    for (( port = lower_port ; port <= upper_port ; port++ )); do
        nc -l -p "$port" 2>/dev/null && break 2
    done
done
28
Chris Down

私の解決策は、ポート0にバインドすることです。これは、ip_local_port_rangeからポートを割り当てるようカーネルに要求します。次に、ソケットを閉じ、構成でそのポート番号を使用します。

これは、カーネルが絶対に必要になるまでポート番号を再利用しないように見えるためです。その後ポート0にバインドすると、別のポート番号が割り当てられます。 Pythonコード:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 0))
addr = s.getsockname()
print addr[1]
s.close()

これにより、ポートの数だけがわかります。 60123

このプログラムを10 000回実行すると(これらを同時に実行する必要があります)、10 000の異なるポート番号が取得されます。したがって、ポートを使用することはかなり安全だと思います。

56
Mark Theunissen

一発ギャグ

私は目的を迅速に果たす素敵な1行をまとめました。これは、任意の範囲の任意の数のポートを取得できるようにします(ここでは読みやすくするために4行に分割しています)。

_comm -23 \
<(seq "$FROM" "$TO" | sort) \
<(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) \
| shuf | head -n "$HOWMANY"
_

1行ずつ

commは、アルファベット順に並べ替えて表示する必要がある2つのファイルの行を比較するユーティリティです。最初のファイルにのみ表示される行、2番目のファイルにのみ表示される行、および共通の行の3つの列を出力します。 _-23_を指定することにより、後者の列を抑制し、最初の列のみを保持します。これを使用して、一連のテキスト行として表される2つのセットの差を取得できます。 commhere について学びました。

最初のファイルは、選択可能なポートの範囲です。 seqは、_$FROM_から_$TO_までのソートされた一連の数値を生成します。結果は(comms要件に準拠するために、数値ではなく)アルファベット順に並べ替えられ、 プロセス置換 を使用して最初のファイルとしてcommにパイプされます。

2番目のファイルは、ssコマンドを呼び出して取得した、ソートされたポートのリストです(_-t_は、TCPポート、_-a_は、すべて-確立され、リスニングしていることを意味します-および_-n_数値-たとえば、_22_をsshに解決しようとしないでください。次に、ローカルアドレスとポートを含むawkの4番目の列のみを選択します。アドレスを分割するためにcutを使用します。 _:_区切り文字を使用して移植し、後者(_-f2_)のみを保持します。次に、commingによるsortingの要件に準拠します。重複する_-u_は重複しません。

これで、開いているポートのソートされたリストができました。これをshuffleして、最初の_"$HOWMANY"_のものを_head -n_で取得できます。

プライベート範囲でランダムに開いている3つのポートを取得(49152-65535)

_comm -23 <(seq 49152 65535 | sort) <(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 3
_

たとえば戻ることができます

_54930
57937
51399
_

ノート

  • ssで_-t_を_-u_に切り替えて、代わりに空きUDPポートを取得します。
  • 利用可能なポートをランダムではなく数値でソートしたい場合は、shufを_sort -n_に置き換えます
13
stefanobaghino
#!/bin/bash
read LOWERPORT UPPERPORT < /proc/sys/net/ipv4/ip_local_port_range
while :
do
        PORT="`shuf -i $LOWERPORT-$UPPERPORT -n 1`"
        ss -lpn | grep -q ":$PORT " || break
done
echo $PORT

クリスダウンへのクレジット

11
Stefan Leisten

どうやら TCP接続はファイル記述子として使用できます Linuxのbash/zshから。次の関数はその手法を使用しており、netcat/telnetを呼び出すよりも高速です。

function EPHEMERAL_PORT() {
    LOW_BOUND=49152
    RANGE=16384
    while true; do
        CANDIDATE=$[$LOW_BOUND + ($RANDOM % $RANGE)]
        (echo "" >/dev/tcp/127.0.0.1/${CANDIDATE}) >/dev/null 2>&1
        if [ $? -ne 0 ]; then
            echo $CANDIDATE
            break
        fi
    done
}

使用方法:出力を変数にバインドし、スクリプトで使用します。 Ubuntu 16.04でテスト済み

root@ubuntu:~> EPHEMERAL_PORT
59453
root@ubuntu:~> PORT=$(EPHEMERAL_PORT)
9
Sandeep

Linuxでは、次のようなことができます。

ss -tln | 
  awk 'NR > 1{gsub(/.*:/,"",$4); print $4}' |
  sort -un |
  awk -v n=1080 '$0 < n {next}; $0 == n {n++; next}; {exit}; END {print n}'

1080を超える最初の空いているポートを見つけます。ssh -Dはループバックインターフェイスにバインドするため、理論的には、ソケットが別のアドレスにバインドしているポート1080を再利用できます。別の方法は、実際に試してバインドすることです。

Perl -MSocket -le 'socket S, PF_INET, SOCK_STREAM,getprotobyname("tcp");
  $port = 1080;
  ++$port until bind S, sockaddr_in($port,inet_aton("127.1"));
  print $port'
4

以下は、使用中のすべてのポートを解放し、3000以降で最初に使用可能なポートを提供する、クロスプラットフォームで効率的な「oneliner」です。

netstat -aln | awk '
  $6 == "LISTEN" {
    if ($4 ~ "[.:][0-9]+$") {
      split($4, a, /[:.]/);
      port = a[length(a)];
      p[port] = 1
    }
  }
  END {
    for (i = 3000; i < 65000 && p[i]; i++){};
    if (i == 65000) {exit 1};
    print i
  }
'

単純にすべての行を結合して、1行にすることができます。別のポート番号から最初のポートを取得したい場合は、iループで割り当てをforに変更します。

MacとLinuxの両方で機能するため、[:.]正規表現が必要です。

4
w00t

これは私が使用するバージョンです:

while
  port=$(shuf -n 1 -i 49152-65535)
  netstat -atun | grep -q "$port"
do
  continue
done

echo "$port"

コマンド shuf -n 1 -i 49152-65535は、ダイナミックレンジの「ランダム」ポートを提供します。すでに使用されている場合は、その範囲内の別のポートが試行されます。

コマンド netstat -atunは、すべての(-a)TCP(-t)およびUDP(-u)ポートをリストし、ホスト名を決定するための時間を無駄にしません(-n)。

3
pfo
while port=$(shuf -n1 -i $(cat /proc/sys/net/ipv4/ip_local_port_range | tr '\011' '-'))
netstat -atun | grep -q ":$port\s" ; do
    continue
done
echo $port

上記の他の回答からの私の組み合わせ。それを得る:

Shuf -n1を使用して、/ proc/sys/net/ipv4/ip_local_port_rangeの範囲(-i)から1つの乱数を取得します。 shufはダッシュ付きの構文を必要とするため、ダッシュを使用してタブを変更するにはtrを使用します。

次に、netstatを使用して、すべての(-a)tcpおよびudp(-u -t)接続を数値(-n)で表示します。\s)次に、他のポートが必要なので続行します。それ以外の場合(grep -qの戻りコード> 0は、whileループを終了し、$ portが設定されています。

1
Winfried Münch

python横になっている場合、私はこれを行います:

port="$(python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1])')";
echo "Unused Port: $port"

この古い趣味の馬での別の実行:

function random_free_tcp_port {
  local ports="${1:-1}" interim="${2:-2048}" spacing=32
  local free_ports=( )
  local taken_ports=( $( netstat -aln | egrep ^tcp | fgrep LISTEN |
                         awk '{print $4}' | egrep -o '[0-9]+$' |
                         sort -n | uniq ) )
  interim=$(( interim + (RANDOM % spacing) ))

  for taken in "${taken_ports[@]}" 65535
  do
    while [[ $interim -lt $taken && ${#free_ports[@]} -lt $ports ]]
    do
      free_ports+=( $interim )
      interim=$(( interim + spacing + (RANDOM % spacing) ))
    done
    interim=$(( interim > taken + spacing
                ? interim
                : taken + spacing + (RANDOM % spacing) ))
  done

  [[ ${#free_ports[@]} -ge $ports ]] || return 2

  printf '%d\n' "${free_ports[@]}"
}

このコードは、netstategrepawk、&alを純粋に移植可能な方法で使用します。最初に取得されたポートのリストを取得するために、呼び出しのみが外部コマンドに対して発行されることに注意してください。 1つ以上の空きポートを要求できます。

:;  random_free_tcp_port
2070
:;  random_free_tcp_port 2
2073
2114

そして任意のポートから始めます:

:;  random_free_tcp_port 2 10240
10245
10293
1
solidsnack

これは、.bashrcにある関数の一部であり、SSHトンネルを動的に作成し、範囲内の任意のポートを使用しようとします。

   lps=( 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 )
   lp=null

   # find a free listening port
   for port in ${lps[@]}; do
      lsof -i -n -P |grep LISTEN |grep -q ":${port}"
      [ $? -eq 1 ] && { lp=$port; break; }
   done
   [ "$lp" = "null" ] && { echo "no free local ports available"; return 2; }
   return $port

YMMV

1
mills013

私は次のときに役立つかもしれないこの小さなPython2/3スクリプトを書きました:

https://github.com/sdenel/find-unused-local-port

0
simon.denel

私の考え...関数はn連続する空きポートを見つけようとします:

#!/bin/bash

RANDOM=$$

# Based on 
# https://unix.stackexchange.com/a/55918/41065
# https://unix.stackexchange.com/a/248319/41065
find_n_ports () {
    local n=$1
    RANDOM=$$
    read lower_port upper_port < /proc/sys/net/ipv4/ip_local_port_range
    local tries=10
    while [ $((tries--)) -gt 0 ]; do
        # Sleep for 100 to 499 ms
        sleep "0.$((100+$RANDOM%399))"
        local start="`shuf -i $lower_port-$(($upper_port-$n-1)) -n 1`"
        local end=$(($start+$n-1))
        # create ss filter for $n consecutive ports starting with $start
        local filter=""
        local ports=$(seq $start $end)
        for p in $ports ; do
            filter="$filter or sport = $p"
        done
        # if none of the ports is in use return them
        if [ "$(ss -tHn "${filter# or}" | wc -l)" = "0" ] ; then
            echo "$ports"
            return 0
        fi
    done
    echo "Could not find $n consecutive ports">&2
    return 1
}

ports=$(find_n_ports 3)
[ $? -ne 0 ] && exit 1
exit 0
0
stefanct