Pode um script de shell imprimir seu argumento, citado como você os escreveria no prompt do shell?

9

Em um script de shell, meu entendimento é que "$@" se expande para os argumentos do script, citando-os conforme necessário. Por exemplo, isso encaminha os argumentos do script para o gcc:

gcc -fPIC "$@"

Ao usar a sintaxe bash pass-to-stdin <<< , "@$" não funciona como eu esperaria.

#!/bin/bash
cat <<< "$@"

Chamando o script como ./test.sh foo "bar baz"

foo bar baz

Eu esperaria

foo "bar baz"

Existe uma maneira de escrever um script de shell que imprime seus argumentos como você os escreveria no prompt do shell? Por exemplo: uma dica sobre qual comando usar em seguida, incluindo os argumentos do script na dica.

    
por Alex Jasmin 31.08.2016 / 18:29

4 respostas

5

Bem, "$@" expande para a lista de parâmetros posicionais, um argumento por parâmetro posicional.

Quando você faz:

set '' 'foo bar' $'blah\nblah'
cmd "$@"

cmd está sendo chamado com esses 3 argumentos: a string vazia, foo bar e blah<newline>blah . O shell chamará a chamada do sistema execve() com algo como:

execve("/path/to/cmd", ["cmd", "", "foo bar", "blah\nblah"], [envvars...]);

Se você quiser reconstruir uma linha de comando do shell (que é o código na linguagem shell) que reproduziria a mesma invocação, você poderia fazer algo como:

awk -v q="'" '
  function shellquote(s) {
    gsub(q, q "\" q q, s)
    return q s q
  }
  BEGIN {
    for (i = 1; i < ARGC; i++) {
      printf "%s", sep shellquote(ARGV[i])
      sep = " "
    }
    printf "\n"
  }' cmd "$@"

Ou com zsh , pedindo diferentes tipos de citações:

$ set '' 'foo bar' $'blah\nblah'
$ print -r -- cmd "${(q)@}"
cmd '' foo\ bar blah$'\n'blah
$ print -r -- cmd "${(qq)@}"
cmd '' 'foo bar' 'blah
blah'
$ print -r -- cmd "${(qqq)@}"
cmd "" "foo bar" "blah
blah"
$ print -r -- cmd "${(qqqq)@}"
cmd $'' $'foo bar' $'blah\nblah'

Ou com zsh , bash ou ksh93 (aqui para bash , YMMV com outras conchas):

$ set '' 'foo bar' $'blah\nblah'
$ printf cmd; printf ' %q' "$@"; printf '\n'
cmd '' foo\ bar $'blah\nblah'

Você também pode usar a opção xtrace do shell que faz com que o shell imprima o que será executado:

$ (PS4=; set -x; : cmd "$@")
: cmd '' 'foo bar' 'blah
blah'

Acima, executamos o comando : no-op com cmd e os parâmetros posicionais como argumento. Meu shell imprimiu-os de uma forma agradável e adequada para reintroduzir o shell. Nem todas as conchas fazem isso.

    
por 31.08.2016 / 18:51
5
'"$@"' expands to the script arguments, quoting them as needed

Não, não é isso que acontece. Chamar um programa recebe uma lista de argumentos, cada argumento sendo uma string. Quando você executa o programa de shell ./test.sh foo "bar baz" , isso cria uma chamada com três argumentos: ./test.sh , foo e bar baz . (O argumento zeroth é o nome do programa; isso permite que os programas saibam sob que nome eles são chamados.) Quoting é um recurso do shell, não um recurso de chamadas de programa. O shell cria essa lista quando faz a chamada.

"$@" copia diretamente a lista de argumentos passados para o script ou função para a lista de argumentos na chamada em que é usado. Não há citação envolvida, pois não há análise de shell feita nessas listas.

Em cat <<< "$@" , você está usando "$@" em um contexto em que uma única cadeia é obrigatória. O operador <<< 'requer uma string, não uma lista de strings. Nesse contexto, o bash pega os elementos da lista e os une com um espaço intermediário.

Para a depuração de script, se você executar set -x ( set +x para desativar), isso ativará um modo de rastreamento no qual cada comando é impresso antes de ser executado. No bash, esse rastreio possui cotações que tornam possível colar o comando de volta em um shell (isso não é verdade para toda implementação de sh ).

Se você tiver uma string e desejar transformá-la em sintaxe de origem de shell que analise a string original, coloque-a entre aspas simples e substitua cada aspas simples da string por '\'' .

for x do
  printf %s "'${x//\'/\'\\'\'}' "
done
echo

A sintaxe de substituição de string é ksh93 / bash / zsh / mksh-specific. Em sh simples, você precisa fazer um loop sobre a string.

for raw do
  quoted=
  while case "$raw" in *\'*) true;; *) false;; esac; do
    quoted="$quoted'\''${raw%%\'*}"
    raw="${raw#*\'}"
  done
  printf %s "'$quoted$raw' "
done
echo
    
por 01.09.2016 / 10:08
2

"$@" expands to the script arguments, quoting them as needed

Bem, mais ou menos. Para fins práticos, deve estar perto o suficiente, e o manual de referência diz que "$@" is equivalent to "$1" "$2" ...

Assim, com os dois parâmetros foo e bar baz , estes seriam semelhantes:

echo "$@"
echo "$1" "$2"
echo "foo" "bar baz"

(Exceto que, se os parâmetros continham caracteres especiais em vez de apenas strings simples, eles não seriam expandidos novamente após a expansão de $@ e $1 ...)

Mas, mesmo se considerarmos $@ substituído pelos parâmetros entre aspas, as citações não estariam lá para echo , da mesma forma que gcc também não recebe as citações.

<<< é uma exceção à regra "$@" == "$1" "$2" ... , é explicitamente mencionado que The result is supplied as a single string to the command on its standard input após passar por expansão de parâmetro e variável e remoção de cotação, entre outros. Portanto, como de costume, <<< "foo" apenas fornece foo como entrada, da mesma forma que somecmd "foo" fornece apenas foo como argumento.

Calling the script as ./test.sh foo "bar baz" [...] I would expect foo "bar baz"

Se as aspas permanecessem, ainda teria que ser "foo" "bar baz" . O shell ou qualquer comando em execução não tem ideia do que foi a cotação quando o comando foi executado. Ou se houve alguma citação, a chamada do sistema apenas recebe uma lista de strings terminadas em null e aspas são apenas uma característica da linguagem shell. Outras línguas podem ter outras convenções.

    
por 01.09.2016 / 12:03
0

Uma solução alternativa para o bash

q='"'; t=( "${@/#/$q}" ); u=( "${t[@]/%/$q}" ); echo ${u[@]}

O Bash não suporta substituições aninhadas, portanto, graças ao link para mostrar como para reatribuir um array. Veja man bash ( link ) para detalhes sobre arrays, expansão e substituição de padrões (sob expansão de parâmetro). / p>

Análise

Bash coloca os parâmetros da linha de comando como uma matriz em $@

q detém o caractere de citação.

Aspas duplas em torno da expansão do parâmetro ${ ... } preservam os parâmetros individuais como elementos distintos e agrupá-los em ( ) permite que você os atribua como uma matriz a uma variável.

/#/$q em uma expansão de parâmetro substitui o início do padrão (como regex ^ ) pelo caractere de aspas.

/%/$q em uma expansão de parâmetro substitui o final do padrão (como regex $ ) pelo caractere de aspas.

Caso de uso: consultando o MySQL para obter uma lista de endereços de email na linha de comando

Há algumas alterações nas instruções acima para usar um caractere de aspas diferente, adicionar vírgulas entre os parâmetros e remover a vírgula final. E claro que estou sendo ruim colocando a senha na invocação do mysql. Então me processe.

q="'"; t=( "${@/#/$q}" ); u="${t[@]/%/$q,}"; v="u.email in( ${u%,} )"
mysql -uprod_program -h10.90.2.11 -pxxxxxxxxxxxx my_database <<END
select ...
from users u
join ....
where $v # <<<<<<<<<<<<<<<<<< here is where all the hard work pays off :-)
group by user_id, prog_id
;
END
    
por 03.06.2018 / 03:28