Parâmetros passados para um script de origem estão errados

2

Um script no AIX que verifica o número de parâmetros transmitidos e reclama se eles não estiverem corretos: -

if [ "$#" -ge 1 ]; then
  ...
else
  echo 'Usage: myscript <a> [b] [c]'
fi

O script define algumas variáveis de ambiente para que seja originado. Com isto quero dizer que escrevo o seguinte na minha linha de comando. Não há segundo script, não estou pesquisando um script de outro script.

. ./myscript.sh

Se eu não passar nenhum parâmetro, espero que ele imprima a declaração de uso, mas ele é executado usando os parâmetros passados para o comando anterior que foi originado (não necessariamente até mesmo este script!).

Para verificar isso, adicionei essa linha no início do meu script para provar que esse era o caso.

echo $# $@

Se eu não passar nenhum parâmetro, ele imprimirá o número e a lista de parâmetros de um script de origem anterior.

Como obtenho para dizer que $# é zero quando eu passar zero parâmetros e fonte?

A menor recriação possível é um script assim: -

myscript.sh

echo $# $@

e depois executá-lo assim: -

. ./myscript.sh a b c

que imprime

3 a b c

execute-o novamente sem parâmetros: -

. ./myscript.sh

que imprime o mesmo

3 a b c

quando você esperaria que ele fosse impresso

0

Se você não fonte o script e apenas executá-lo assim: -

./myscript.sh

funciona como esperado e imprime

0

Espero que esclarece o assunto em questão.

Como parece que estou recebendo muitas respostas para explicar por que o acima não funciona e sem respostas para me dizer o que devo fazer, pensei em tentar mais uma vez explicar o que estou tentando fazer. fazer.

Requisito

Eu preciso escrever um script que espera pelo menos um parâmetro e reclama se ele não for invocado com um parâmetro (veja o primeiro bloco de código na minha pergunta acima), e esse script precisa definir algumas variáveis de ambiente. Devido a esse segundo requisito, presumo que preciso fornecer o script para que as variáveis de ambiente ainda sejam definidas quando o script for concluído. Como você pode escrever um script que pode dizer quando não foi passado parâmetros que também é capaz de definir uma variável de ambiente?

    
por Morag Hughson 03.05.2018 / 13:09

3 respostas

2

Em

. path/to/script at least one argument

Como outros já disseram, dependendo do shell, esses argumentos extras são ignorados (Bourne, Almquist) ou substituem o conjunto atual de parâmetros posicionais ( $@ , $1 , $2 ...) para o interpretação desse script (a maioria dos outros shells). POSIX não especifica o comportamento quando argumentos extras são passados para . . Nesse caso, também há alguma variação entre os shells quanto a se qualquer alteração nos parâmetros posicionais (como com set a b ) feita dentro do script permanecerá depois que . terminar.

Em:

. path/to/script

no entanto, o comportamento é especificado. Os parâmetros posicionais são necessários não para serem afetados, portanto, permanecerá o mesmo de antes para a interpretação do script.

Se você quiser que os parâmetros posicionais sejam uma lista vazia durante a interpretação desse script, será necessário redefini-lo antes ou use uma função:

set -- # or shift "$#"
. path/to/script

(embora os parâmetros posicionais sejam perdidos). Ou use uma função:

dot() { file=$1; shift; . "$file"; }
dot path/to/myscript

No momento em que o comando . é executado, os parâmetros posicionais serão os da função. Após o shift , serão os argumentos passados para a função menos o primeiro, portanto, no acima, uma lista vazia; mas isso significa que com essa função você pode portavelmente passar uma lista arbitrária de parâmetros posicionais para o script originado como em:

dot path/to/myscript extra args

Onde os extra args estarão disponíveis como $1 e $2 no script de origem.

E se myscript chamar set para modificar a lista de parâmetros posicionais, isso afetará apenas os parâmetros posicionais da função, e essas alterações não persistirão depois que a função retornar.

    
por 04.05.2018 / 12:47
2

Texto de ajuda do Bash para . :

.: . filename [arguments]
    Execute commands from a file in the current shell.

    Read and execute commands from FILENAME in the current shell.  The
    entries in $PATH are used to find the directory containing FILENAME.
    If any ARGUMENTS are supplied, they become the positional parameters
    when FILENAME is executed.

Observe a frase: " se qualquer argumento for fornecido". Sem argumentos, os parâmetros posicionais atuais permanecem.

$ cat /tmp/sourceme.sh     
printf "%d: " "$#"
printf "<%s> " "$@"
printf "\n"
$ bash -c 'set -- aa bb; . /tmp/sourceme.sh'
2: <aa> <bb> 
$ bash -c 'set -- aa bb; . /tmp/sourceme.sh foo bar'
2: <foo> <bar> 

Todos os outros shells que testei (zsh, ksh) se comportam de maneira semelhante. Exceto para dash , que sempre deixa os parâmetros posicionais originais no lugar.

$ dash -c 'set -- aa bb; . /tmp/sourceme.sh foo bar'
2: <aa> <bb> 

Não consigo ver qualquer menção sobre a redefinição dos parâmetros posicionais na definição POSIX de . então parece que dar argumentos para . não é padrão para começar.

Em vez de um script de origem, você poderia usar uma função, pois eles podem aceitar argumentos em um shell padrão. Nesse caso, você procuraria um arquivo que definisse a função.

Por exemplo um arquivo ( define_fun.sh ) com a definição da função:

#!/bin/sh
f() {
    echo "args: $# $@"     # just a test
    if [ "$#" = 0 ]; then 
        echo please give at least one argument
        return 1
    fi;
    ENVVAR=$1          # this should be visible to the whole shell  
    export ENVVAR      # and any commands called later
} 

E então use:

$ . define_fun.sh      # source the file to load the func in the current shell

$ f                    # actually run it: this'll complain
$ f 123 456            # this should set ENVVAR
$ echo $ENVVAR         # and this will print '123'

Você poderia colocar a definição da função em um dos arquivos de inicialização do shell ( ~/.profile ? Depende do shell.)

    
por 03.05.2018 / 14:34
0

A transmissão de argumentos para o comando dot builtin é uma extensão do Korn Shell que não é concedida para funcionar em outros shells.

Como o padrão POSIX não menciona $ # e $ @ para o comando dot, os argumentos para o shell atual são visíveis para o comando dot, portanto, se você chamar seu shell, por exemplo, como:

myshell -s a b c

e depois ligue

. ./myscript.sh

espera-se que este seja impresso

3 a b c
    
por 03.05.2018 / 16:23