O que estou fazendo errado ao tentar escrever um script bash que retorna o número da próxima porta disponível?

1

Estou tentando escrever um script que usa um número de porta como argumento. Ele retorna a próxima porta que não está atribuída a nada e a verifica usando o arquivo /etc/services . Se a porta é tomada (ou seja, listada no documento), ela adiciona uma e, em seguida, tenta novamente. Eu não consigo obter este script para retornar qualquer coisa para "encontrado" - sempre é igual a 0, então eu nunca entro no loop while. O que estou fazendo errado?

#!/bin/bash

port=$1
found=$(cat /etc/services | grep -o '[[:space:]]$port/' | wc -l)

while [ $found -ge 1 ]; do
    $port=$($port+1)
#done

echo "found: $found"
echo "port: $port"

(ignore o comentário na frente de done , esse não é o problema)

    
por Jeff Schaller 04.05.2017 / 00:22

2 respostas

2

Visão geral

Existem apenas alguns pequenos erros com este script e algumas coisas estilísticas que eu mudaria. Vamos percorrer o original linha por linha:

#!/bin/bash

port=$1

Uma típica linha #! e uma atribuição simples, começamos bem.

found=$(cat /etc/services | grep -o '[[:space:]]$port/' | wc -l)

Esta linha tem um pequeno problema que pode não ser percebido à primeira vista. Como $port está em citações strongs , '…$port' , ele não se expandirá para seu valor. Você vai querer usar aspas fracas , "…$port" . Além disso, você realmente não precisa de cat aqui. grep usa um nome de arquivo como argumento, mas se não, você sempre pode usar o redirecionamento ( grep 'pattern' < /etc/services ). Além disso, grep -c escreve uma contagem de linhas correspondentes, então wc -l é redundante aqui também.

while [ $found -ge 1 ]; do
    $port=$($port+1)
done

O loop parece quase bom. Você desejará citar as entidades no teste no caso de $found poder estar vazio. Além disso, você atribui com port= , não $port= (mas usou o formulário correto acima, então presumo que seja apenas um erro de digitação). Finalmente $($port+1) significa (se port é 22 , por exemplo) "A saída do comando 22+1 ". Claramente você quer "O resultado da expressão aritmética 22+1 ", que é o mesmo $(($port+1)) .

echo "found: $found"
echo "port: $port"

Estas linhas estão bem como estão.

Considerando a lógica

Agora, se você fizer todas as alterações acima para que o programa esteja sintaticamente correto, ele ainda não fará o que você deseja. Você não lista uma saída "correta", então estou supondo que você queira:

found: <the number of occurrences of $1 in /etc/services>
port: <the next free port>

Se essas suposições estiverem incorretas, me avise nos comentários e eu os editarei de acordo.

O script como ele nunca retornará se a porta fornecida aparecer em /etc/services , porque você nunca atualiza found . Então você pode pensar que seria prudente copiar a linha found=… para o loop, mas isso não é verdade! Se você fez isso, o script sempre retornará found: 0 . Acho que a melhor coisa a fazer aqui é criar uma função :

found () {
    grep -co "[[:space:]]$1/" /etc/services
}

mas agora o valor de saída dessa função pode funcionar diretamente como uma condição. Você vai querer aquietar a saída para que ela não apareça junto com a sua e, em seguida, você não precisará mais dos sinalizadores -co , mas desejará -q :

while found "$port"; do

Em seguida, para retornar o número de ocorrências da porta de entrada, seria

echo "found: $(found "$1")"

ou você pode usar printf .

Colocando tudo junto

Acho que este é o script que você estava tentando escrever:

#!/bin/sh

found () {
    grep -q "[[:space:]]$1/" /etc/services
}

port=$1

while found "$port"; do
    port=$(($port+1))
done

printf 'found: %d\nport %d\n' "$(found "$1")" "$port"

Bônus

Como eu estava escrevendo isso, minha reação inicial foi que as linhas de contagem correspondentes a um determinado padrão poderiam ser feitas em awk , bem como grep . Ocorreu-me então que todo o programa poderia ser escrito em awk . Como um bônus, aqui está uma implementação disso (apesar de aqui eu assumir que /etc/services é classificado pelo número da porta):

#!/usr/bin/awk -f

BEGIN {
    FS = "[ \t/][ \t/]*"
    ARGC = 2
    PORT = ARGV[1]
    OPORT = PORT
    ARGV[1] = "/etc/services"
}
($2 == OPORT) {
    FOUND = FOUND + 1
}
(NEXT == 0 && NR > 1 && $2 > PORT) {
    NEXT = ($2 > PORT + 1) ? PORT + 1 : NEXT
    PORT = $2
}
END {
    printf "found: %d\nport: %d\n", FOUND, (FOUND == 0) ? OPORT : NEXT
}
    
por 04.05.2017 / 04:40
-1

Menos a parte de matemática, aqui está um POC rápido e sujo para realizar o que você está procurando, mas usando uma declaração "if" em vez de condições de loop "while" para sua lógica.

#!/bin/bash

port=$1
echo $port | while read a; do
found=$(cat /etc/services | grep -Eo '[0-9]{1,5}/' | sed 's/^/ /' | grep ' '$a'/')
if [[ $found ]];
then echo port $a is being used
else echo port $a is not being used
fi
done

Edit: O problema foi derivado da variável $ found. as aspas simples no grep fazem o $ port ser interpretado literalmente, então seu grepping para o string '$ port /' em vez de '80 /' por exemplo. corrigir: basta alterar as aspas simples para aspas duplas.

encontrado = $ (cat / etc / services | grep -o "[[: espaço:]] $ port /" | wc -l)

Espero que isso ajude

    
por 04.05.2017 / 17:30