Existe uma maneira de obter argumentos shell * real (não interpretados) em uma função ou script?

3

Eu tenho uma função posix que eu uso no bash shell do Git no Windows para transformar os caminhos no estilo DOS em caminhos normais do estilo Unix. Como os caminhos no estilo DOS usam uma barra invertida como separador, tenho que citar o argumento path para evitar que as barras invertidas sejam usadas pelo shell para indicar o próximo caractere como um literal. Existe alguma maneira de obter o argumento não interpretado dentro da minha função, para que eu não precise citá-lo?

Aqui está minha função, se ajudar:

function posix() {
  echo $1 | sed -e 's|\|/|g' | sed -re 's|^(.)\:|/\L|'
}

(A propósito, eu agradeço quaisquer comentários com dicas para melhorar a função de outras formas não relacionadas à resolução do problema de citação / interpretação de shell.)

    
por iconoclast 28.09.2012 / 18:17

3 respostas

6

Os argumentos de shell não interpretados são $1 , $2 , etc. Você precisa colocar a expansão entre aspas duplas na maioria dos contextos, para evitar que o valor do parâmetro seja expandido ainda mais. "$@" fornece a lista de todos os parâmetros.

Por exemplo, se você quiser passar um argumento do shell script para sua função, chame-o assim:

first_argument_as_filename_in_unix_syntax=$(posix "$1")

As aspas duplas são necessárias. Se você escreve posix $1 , então o que você está passando não é o valor do primeiro parâmetro, mas o resultado da execução da divisão de palavras e globbing no valor do primeiro parâmetro. Você precisará usar a cotação adequada ao chamar o script também. Por exemplo, se você escrever isso no bash:

myscript c:\path with spaces\somefile

, os argumentos reais e não interpretados para myscript serão c:path , with e spacessomefile . Então não faça isso.

Sua função posix está errada, novamente porque falta aspas duplas em torno de $1 . Sempre coloque aspas duplas em torno das substituições de comando e variável: "$foo" , "$(foo)" . É mais fácil lembrar essa regra do que as exceções em que você realmente não precisa das aspas.

echo faz seu próprio processamento em alguns casos, e chamar processos externos é lento (especialmente no Windows). Você pode fazer todo o processamento dentro do bash.

posix () {
  path="${1//\//}"
  case "$path" in
    ?:*) drive="${p:0:1}"; drive="${drive,}"; p="/$drive/${p:2}";;
  esac
  printf %s "$p"
}

O recurso do zsh ao qual o jw013 faz alusão não faz o que você acha que faz. Você pode colocar noglob na frente de um comando e zsh não executa globbing (ou seja, geração de nome de arquivo, ou seja, expansão de curingas) nos argumentos. Por exemplo, em zsh, se você escrever noglob locate *foo*bar* , então locate será chamado com o argumento *foo*bar* . Você normalmente ocultaria o noglob incorporado por trás de um alias. Esse recurso é irrelevante para o que você está tentando fazer.

    
por 29.09.2012 / 02:06
3

Enquanto outras respostas podem estar corretas em afirmar que você não pode receber entradas de shell "não interpretadas" através dos meios que elas mencionam, elas estão erradas em negá-las categoricamente como uma possibilidade. Você certamente pode recebê-lo antes que o shell o interprete, se você instruir o shell a não interpretá-lo. O humilde POSIX heredoc torna isso simplesmente possível:

% sed -e 's@\@/@g' -e 's@\(.\):\(.*\)@/drive/@' <<'_EOF_'     
> c:\some\stupid\windows\place
> _EOF_
/drive/c/some/stupid/windows/place

EDIT1:

Para passar essa string para uma função shell como um argumento shell, você precisará armazená-la em uma variável shell. Geralmente você não pode simplesmente var=<<'HEREDOC' infelizmente, mas o POSIX especifica o argumento -r para o read builtin:

% man read

POSIX PROGRAMMER'S MANUAL

...

By default, unless the -r option is specified, backslash ( '\' ) shall act as an escape character, as described in Escape Character (Backslash) . If standard input is a terminal device and the invoking shell is interactive, read shall prompt for a continuation line when:

  • The shell reads an input line ending with a backslash, unless the -r option is specified.

  • A here-document is not terminated after a new line is entered.

Quando combinados, read e heredoc tornam isso um assunto trivial e portátil também, embora não pareça muito intuitivo no início:

% _stupid_mspath_fix() { 
> sed -e 's@\@/@g' -e 's@\(.\):\(.*\)@/drive/@' <<_EOF_
>> ${1}
>> _EOF_
> }
% read -r _stupid_mspath_arg <<'_EOF_'                    
> c:\some\stupid\windows\place
> _EOF_
% _stupid_mspath_fix ${_stupid_mspath_arg}
/drive/c/some/stupid/windows/place

EDIT2:

Provavelmente você notou a diferença entre os dois heredocs no segundo exemplo. O terminador heredoc _EOF_ dentro da função é sem aspas, enquanto o alimentado para read é citado com aspas simples. Dessa maneira, o shell é instruído a executar a expansão no heredoc com um terminador sem aspas, mas não deve fazê-lo quando seu terminador é citado. Ele não quebra ao expandir o heredoc não-referenciado na função porque o valor da variável que ela expande já está definido como uma string entre aspas e não é analisado duas vezes.

Provavelmente, o que você deseja fazer envolve canalizar o caminho do Windows da saída de um comando para a entrada de outro dinamicamente. A substituição de comandos dentro de um heredoc torna isso possível:

% _stupid_mspath_fix() { 
> sed -e 's@\@/@g' -e 's@\(.\):\(.*\)@/drive/@' <<_EOF_
>> ${1}
>> _EOF_
> }
% read -r _stupid_mspath_arg <<'_EOF_'                    
> c:\some\stupid\windows\place
> _EOF_
% _stupid_mspath_fix ${_stupid_mspath_arg}
/drive/c/some/stupid/windows/place    
% read -r _second_stupid_mspath_arg <<_EOF_                    
> $(printf ${_stupid_mspath_arg})
> _EOF_
% _stupid_mspath_fix ${_second_stupid_mspath_arg}
/drive/c/some/stupid/windows/place

Então, basicamente, se você puder produzir com segurança as barras invertidas de algum aplicativo (usei printf acima), em seguida, executar esse comando em $(...) e incluí-lo em um heredoc sem aspas passado para outro aplicativo que possa aceitar com confiança o barras invertidas como entrada (como read e sed acima) irão ignorar completamente a análise das barras invertidas do shell. Se os aplicativos podem ou não lidar com as barras invertidas como entrada / saída é algo que você terá que descobrir por si mesmo.

Não é estritamente relevante para a pergunta:

Na resposta de Gilles, ele recomenda a forma de expansão do parâmetro ${var/search/replace} , que, embora legal, não é POSIX. É definitivamente um basismo. Não importaria para mim, mas em suas edições ele reteve o nome da função posix () , e isso pode ser enganoso para alguns.

Nessa nota, a função posix () da postagem original faz uso do muito conveniente argumento de sed -r da regex estendida, mas isso também não é, infelizmente, POSIX. POSIX não especifica um argumento de expressão regular estendido para sed , e seu uso é, portanto, possivelmente não confiável.

Minha conta no estouro da pilha também tem apenas alguns dias, mas eu postei algumas respostas lá lidando especificamente com a expansão do parâmetro POSIX que você pode encontrar vinculado a partir da minha página de perfil, e no qual incluo citações das diretrizes do POSIX e seus links. Você também encontrará algumas em que eu demonstro outros usos do heredoc , como ler um script de shell inteiro em uma variável de shell, analisá-lo e manipulá-lo programaticamente e finalmente executar sua nova versão, tudo feito de outro script ou shell função. Apenas dizendo.

    
por 23.05.2017 / 14:40
2

Você não pode ter a entrada " uninterpreted " do processo do shell. O que você digita na linha de comando é apenas uma sequência de caracteres que deve ser interpretada com precisão pelo shell. Você não pode fazer com que o shell passe seus caracteres literais para uma função ou comando, porque tem para interpretá-los para saber qual comando / função invocar e com quais argumentos! Quando você digita algo no prompt de comando, você tem que aceitar que você tem que digitar de acordo com as regras de interpretação do shell (porque você está usando um shell!).

As regras da interpretação de shell são muito úteis. Ao invés de ficar no seu caminho, eles lhe dão uma maneira de instruir o shell a fazer o que quiser, incluindo a especificação de argumentos arbitrários para as coisas. Sem interpretação, não há como o shell extrair uma ação para executar precisamente a partir de sua entrada. Interpretação implica em caracteres especiais (como espaço), e sem escapar, não haveria como passar esses caracteres para um comando.

Com relação à interpretação em si, sempre encontrei a documentação do bash neste ser muito bom. Resumidamente, coisas como expansão de til acontecem muito cedo (e expansão de til somente em certas situações). Então, substituição de variáveis e divisão de palavras acontecem e, finalmente, globbing.

    
por 29.09.2012 / 13:14