Shift argumentos bash da direita

0

Eu tenho um script para o qual os usuários prefiram, em vez de acrescentar, argumentos, ou seja, podem chamar command C , command B C , command A B C e assim por diante.

Eu gostaria de poder simplesmente passar esses argumentos da direita, da mesma maneira que você pode movê-los da esquerda com shift .

Estou visualizando um comando shift-right que se comporta assim:

echo "$@"    # A B C
shift-right
echo "$@"    # A B
shift-right
echo "$@"    # A
shift-right
echo "$@"    # 
echo "$#"    # 0

Existe uma maneira limpa de conseguir isso? Eu sei que posso contornar isso, mas uma solução semelhante a shift seria muito mais agradável e simples.

Em resposta ao comentário do problema XY, meu caso de uso específico é um comando que usa uma porta ou um host e uma porta, por exemplo, command 123 ou command remotehost 123 . Eu não quero que os usuários precisem especificá-los na ordem inversa (porta e host).

Seria bastante claro dizer algo como (não testado, obviamente):

port=${@: -1}
shift-right
host=${1:-localhost}

Realmente, estou curioso sobre a questão em geral, mesmo que haja uma maneira melhor de resolver esse exemplo específico.

Aqui está uma maneira razoavelmente limpa de lidar com o caso de dois argumentos sem shift-right , apenas para referência:

port=${@: -1}
host=${2:+$1}
host=${host:-localhost}

Mas esperamos que você possa entender como isso se torna mais complicado à medida que o número de argumentos aumenta.

    
por dimo414 01.04.2016 / 01:33

2 respostas

3

Se a lista de parâmetros posicionais for:

$ set -- 0wer 1wdfg 2erty 333 4ffff 5s5s5

Então, isso imprimirá os argumentos sem o último:

$ echo "${@:1:$#-1}"
0wer 1wdfg 2erty 333 4ffff

É claro que os parâmetros também podem ser definidos para isso:

$ set -- "${@:1:$#-1}"
$ echo $@
0wer 1wdfg 2erty 333 4ffff

Isso funciona na versão bash 2.0 ou superior.

Para outros shells mais simples, você precisa de um loop (um tanto complicado) para remover o último parâmetro:

unset b; for a; do set -- "$@" ${b+"$b"}; shift; b="$a"; done
    
por 01.04.2016 / 02:30
1

Você pode simplesmente verificar o número de argumentos e processá-los na ordem inversa, por exemplo, para o caso simples de dois argumentos em que o argumento primeiro é opcional:

usage() {
  cat << EOF
Usage: $0 [host] port
EOF
}

[ "$#" -eq 0 ] && { usage; exit 1;}

if [ "$#" -gt 1 ]; then
  myhost="$1"
  shift
fi

myport="$1"

No entanto, você menciona a ideia de três argumentos também. Não é realmente trivial ter três argumentos opcionais determinados por posição - e se você quiser especificar o primeiro e o terceiro, mas não o segundo? Esses cenários são quase impossíveis de serem tratados apenas com parâmetros posicionais, mas muito simples com o uso correto de getopts .

usage() {
  cat << EOF
usage: $0 [OPTIONS]

    -p    port number (required)
    -h    hostname (optional)
EOF
}

while getopts :p:h: opt; do
  case "$opt" in
    p)
      myport="$OPTARG"
      ;;
    h)
      myhost="$OPTARG"
      ;;
    :)
      printf %s\n "Argument required for option -$OPTARG" >&2
      usage >&2
      exit 1
      ;;
    \?)
      printf %s\n "Unknown option -$OPTARG" >&2
      usage >&2
      exit 1
      ;;
  esac
done
shift "$((OPTIND-1))"

if [ -z "$myport" ]; then
  printf %s\n "You must supply a port" >&2
  usage >&2
  exit 1
fi
    
por 01.04.2016 / 02:43

Tags