Passa dois comandos shell (canalizados) como um parâmetro para uma função shell

3

Eu defini as seguintes funções do shell:

success() {
  printf "[3[32mSUCCESS3[0m]\n"
}

failure() {
  printf "[3[31mFAILURE3[0m]\n"
}

try() {
  result=$($* 2>&1)
  if [ $? -ne 0 ]; then
    failure
    echo $result
    exit 1
  fi
  success
}

Isso permite que eu execute silenciosamente qualquer comando, capturando toda a saída e exiba uma mensagem SUCCESS ou FAILURE . Somente se o comando falhar, a saída do comando será exibida para depuração:

try rm -r $tmp

Mas acabei de perceber que está limitado a um único comando e falha miseravelmente por um comando canalizado para outro com | :

try git archive --format=tar HEAD | tar xf - -C $tmp

Porque try é executado apenas no comando git e, em seguida, a saída de try é canalizada para tar , em vez da saída de git .

É possível passar os dois comandos git ... | tar ... como um único parâmetro para try , capturando a saída de ambos e verifique se ambos git e tar retornaram 0 ?

Qualquer outra consideração, talvez, para alcançar meu objetivo?

    
por Benjamin 12.09.2014 / 16:05

2 respostas

1

Se você quiser passar um pipeline como git … | tar … (é um único comando que contém dois subcomandos, não dois comandos separados) diretamente como um argumento para uma função, você precisará criar uma string contendo esse comando e usar o eval embutido na função para executar essa string como um comando shell.

Cuide da cotação correta. Para o argumento, você está construindo uma string que é um script mini-shell usando as mesmas variáveis que o script anexo. Em particular, se uma variável contém um nome de arquivo (como tmp here), você precisa passar a expansão de variável, não o valor da variável, porque um nome de arquivo não é um fragmento de shell, então você precisa de '…"$tmp"…' , não "…$tmp…" . Ao avaliar, você precisa passar a string exata para eval , não uma string que já sofreu expansão. Em particular, $* é praticamente sempre errado; leia Por que meu script de shell engasgar com espaço em branco ou outros caracteres especiais?

try () {
  result=$(eval "$1" 2>&1)
  if [ $? -ne 0 ]; then
    failure
    echo $result
    exit 1
  fi
  success
}
try 'git archive --format=tar HEAD | tar xf - -C "$tmp"'

Uma abordagem alternativa é colocar o comando composto em uma função. Mais uma vez, cuide da cotação correta. Use "$@" para avaliar os parâmetros da função como um comando simples (alias, função, comando interno ou externo com argumentos).

try () {
  result=$(eval "$1" 2>&1)
  if [ $? -ne 0 ]; then
    failure
    echo $result
    exit 1
  fi
  success
}
archive_to_directory () {
  git archive --format=tar HEAD | tar xf - -C "$1"'
}
try archive_to_directory "$tmp"

O status de um pipeline é o status do lado direito; o status do lado esquerdo é ignorado. No bash (mas não no sh), você pode acessar o status de todos os comandos em um pipeline através do PIPESTATUS variable .

try () {
  result=$(eval "$1" 2>&1)
  if [ $? -ne 0 ]; then
    failure
    echo $result
    exit 1
  fi
  success
}
archive_to_directory () {
  git archive --format=tar HEAD | tar xf - -C "$1"'
  [[ -n ${PIPESTATUS[*]//[0 ]/} ]]
}
try archive_to_directory "$tmp"
    
por 14.09.2014 / 03:12
1

Você terá que adicionar algumas citações na linha de comando, não há como evitar isso

try "git archive --format=tar HEAD | tar xf - -C $tmp"

Então, na função, uma vez que metachars de shell (como | ) não são especiais quando armazenados em uma variável, então você precisará de eval

try() {
  if result=$(eval "$*" 2>&1); then
    success
  else
    failure
    echo $result
    exit 1
  fi
}

Tenho a sensação de que não entendi direito. Espero que alguém me corrija.

    
por 12.09.2014 / 16:53