Bash: Analisa várias linhas em comandos de linha única

6

Eu tenho que escrever um script Bash que verifique se outro script Bash contém uma certa linha de comando. Como o Bash permite dividir uma linha de comando em várias linhas, meu script deve conseguir mesclar as linhas correspondentes antes que a correspondência real de padrões possa ocorrer.

Como faço para analisar todas as linhas múltiplas em comandos de linha única em um script Bash?

Exemplo

Eu quero verificar se um determinado script contém o comando ls - e se ele contém o comando ls , quero saber quais parâmetros são passados para o Comando ls . Para responder a essa pergunta, eu poderia usar sed . Mas, portanto, tenho que mesclar todos os comandos de várias linhas primeiro.

Entrada:

# My comment \
ls \
-a \
-l

Saída:

# My comment \
ls -a -l

Exemplo para uma saída inválida:

# My comment ls -a -l
    
por Lugaxx 09.09.2015 / 14:41

5 respostas

7

Pouco antes do shell do shell, eu respondi a uma pergunta no StackOverflow sobre a eliminação de comentários em scripts do bash. Minha resposta usou o truque simples de criar uma função colocando o conteúdo do arquivo de script dentro de tmp_() { ... } e, em seguida, usando declare -f tmp_ para imprimir bem a função. Na saída bem impressa, não há comentários e as linhas continuadas com uma nova linha invertida foram resolvidas para linhas únicas. (Exceto dentro da substituição do comando backticked).

Outra reformatação também é feita. Por exemplo, comandos compostos são divididos em várias linhas. E algumas formas de continuação de linha não são reformatadas; por exemplo, uma linha que termina com um símbolo de pipe não é alterada. Mas deve satisfazer o caso de uso nesta questão. (Veja o exemplo abaixo.)

É claro que a definição da função precisa ser avaliada, o que significa que o script que é bem impresso pode incluir um ataque de injeção. No código sugerido, a definição da função é avaliada por meio do recurso bash, que permite que as funções sejam exportadas e compartilhadas com um processo filho. Na época em que escrevi este pequeno hack, acreditei que o mecanismo era mais seguro do que chamar eval , mas, no fim das contas, estava errado.

Desde o shellshock, tem havido uma série de aprimoramentos no code bash para importar definições de funções, fechando as portas em pelo menos alguns ataques de injeção, mas claramente não há garantia de que o procedimento é completamente seguro.

Se você for executar o script que está sendo analisado, o uso desse procedimento para uma impressão bonita provavelmente não aumenta sua vulnerabilidade; um invasor poderia simplesmente inserir o código perigoso diretamente no script e não haveria necessidade de passar por obstáculos para ocultar o ataque de uma maneira que pudesse ignorar as verificações de segurança no código de importação da função.

Mesmo assim, você deve pensar cuidadosamente sobre questões de segurança, tanto com este pequeno programa quanto com quaisquer planos que você possa ter para executar scripts arbitrários.

Aqui está a versão do pretty-printer que funciona com um bash pós-shellshock-patched (e não funciona com as versões anteriores do bash):

env "BASH_FUNC_tmp_%%=() {
$(<script_name)
}" bash -c 'declare -f tmp_' | tail -n+2

Substitua o nome do arquivo que contém o script por script_name na segunda linha. Você pode querer ajustar o comando tail ; ele remove o nome da função do wrapper, mas não remove as chaves que cercam o corpo do script.

A versão original, que funcionará nas versões pré-shell do bash, pode ser encontrada na resposta SO referenciada.

Amostra.

Testado com base na entrada fornecida por Stéphane Chazelas :

{ 
    echo \;
    echo a#b;
    echo 'foo\
bar';
    cat  <<EOF
thisis joined
this 'aswell'
$(ls -l)
EOF

    cat  <<'EOF'
this is\
not joined
EOF

    echo "$(ls -l)";
    echo 'ls \
-l'
}

Isso difere da saída sugerida por Stéphane:

  • As linhas foram recuadas e muitas foram terminadas com ponto e vírgula. Espaços em branco foram adicionados e / ou excluídos em muitas linhas.
  • cat << E\OF foi alterado para cat <<'EOF' , que é semanticamente idêntico.
  • A linha de continuação aninhada na substituição do comando backticked no final não foi modificada. (A linha de continuação na substituição do comando $(...) é eliminada.)
por 10.09.2015 / 05:44
2

Esta não é realmente uma resposta, apenas uma observação sobre as coisas a considerar para que uma solução funcione no caso geral.

#! /bin/sh
echo \
echo a#\
b
echo 'foo\
bar'
cat << EOF
this\
is joined
this 'as\
well'
$(ls \
-l)
EOF
cat << E\OF
this is\
not joined
EOF
echo "$(ls \
-l)"
echo 'ls \
-l'

Meu entendimento da intenção da questão seria que ela fosse transformada em:

#! /bin/sh
echo \
echo a#b
echo 'foo\
bar'
cat << EOF
thisis joined
this 'aswell'
$(ls -l)
EOF
cat << E\OF
this is\
not joined
EOF
echo "$(ls -l)"
echo 'ls -l'
    
por 09.09.2015 / 18:05
2

Isso funciona em mais casos agora; veja se ele faz o que você está esperando:

sed ':loop /^[^#].*[^\]\$/N; s/\\n//; t loop' input

Imprime todas as linhas por padrão; se encontrar uma barra invertida (com escape porque é um caractere especial) no final de uma linha ($) - e não houver uma marca no início da linha, junte-a à próxima linha (N) modificada por pesquisar & substituindo a barra invertida (escapou novamente) e o caractere de nova linha com nada . Se a pesquisa & replace fez algo, depois voltou para a tag "loop" e executou novamente a pesquisa.

Entrada:

# My comment \
ls \
-al

# leading comment
echo some \
long \
text
# trailing comment

ls -al

Saída:

# My comment \
ls -al

# leading comment
echo some long text
# trailing comment

ls -al
    
por 09.09.2015 / 17:00
0

O histórico converte comandos típicos de várias linhas em uma única linha. Basta digitar as coisas e, em seguida, emitir um "cursor para cima" para ver a conversão.

    
por 09.09.2015 / 14:55
0

Para substituir novas linhas por espaços, você pode apenas

tr '\n' ' '

Isso pode deixar algumas barras invertidas no código, então você provavelmente deve substituir novas linhas precedidas por barras invertidas por espaços

 perl -pe 's/\\n/ /'

Isso ainda pode dar errado (documentos AQUI, comentários, etc.), mas para obtê-lo 100% correto, você teria que escrever um analisador de shell completo.

    
por 09.09.2015 / 14:56