find e grep produzem resultados diferentes se executados a partir de um arquivo de script ou linha de comando

-1

Estou usando esses comandos para pesquisar em vários PDFs, considerando um caminho de arquivo:

>>find /home/ad0x/Documents/Skola/Flervariabel/Tentor -name '*.pdf' -exec sh -c 'pdftotext "{}" - | grep --with-filename --label="{}" --color "phrase"' \;

Onde phrase é o termo que você deseja pesquisar nos pdfs. Isso funciona como esperado. Eu recebo todas as ocorrências da palavra "volym".

Quandotentofazeramesmacoisaemumscript.sh(search.sh)

#!/bin/bashread-p"Enter term to search for: " phrase
find /home/ad0x/Documents/Skola/Flervariabel/Tentor -name '*.pdf' -exec sh -c 'pdftotext "{}" - | grep --with-filename --label="{}" --color "$phrase"' \;
echo "Search completed"
 >>./search.sh
 >>Enter term to search for:volym

Produz todas as linhas em cada pdf. A saída:

Suspeito que tenha algo a ver com o read que interpreta a entrada, mas não encontrei uma solução para o meu problema on-line.

    
por ad0x 09.10.2018 / 11:46

1 resposta

2

O culpado direto é $phrase entre aspas simples. Este não é o único problema.

O que acontece

Este é o código relevante (note que eu uso reticências para a parte menos interessante; essa linha deve ser entendida por humanos, e não diretamente executada em uma concha):

find … -exec sh -c 'pdftotext "{}" - | grep --with-filename --label="{}" --color "$phrase"' \;

O shell que interpreta o script contém o valor da variável phrase ; digamos que o valor seja volym . No comando acima, tudo o que está entre aspas simples é deixado intacto porque é assim que funciona a cotação única; então $phrase ainda não está expandido. O shell só analisa \ , o que informa o seguinte: ; não serve para separar comandos, ele deve ser tratado como um argumento de linha de comando para find .

Quando o utilitário find é executado, isso é o que ele vê como argumentos (a partir de 0º, ou seja, o find ; um argumento por linha, exceto que denota vários argumentos menos interessantes):

find
…
-exec
sh
-c
pdftotext "{}" - | grep --with-filename --label="{}" --color "$phrase"
;

Observe que a última, mas uma linha, é um argumento longo.

Suponhamos que foo.pdf seja encontrado e -exec faça seu trabalho. Todos os argumentos entre -exec e ; se tornam um novo comando depois que {} é substituído por foo.pdf . O novo comando será (novamente, a partir do 0º argumento; um argumento por linha):

sh
-c
pdftotext "foo.pdf" - | grep --with-filename --label="foo.pdf" --color "$phrase"

Portanto, sh é executado, obtém -c e, portanto, sabe que o próximo argumento deve ser executado como se fosse digitado na linha de comando:

pdftotext "foo.pdf" - | grep --with-filename --label="foo.pdf" --color "$phrase"

Este é o momento em que $phrase é expandido. Ele se expande para nada (a última palavra se torna "" ) porque não foi definida neste shell. Expandiria para volym se você exportasse a variável em seu script; mas você não fez. Eu não exportaria embora; na minha opinião, neste caso, a exportação poluiria desnecessariamente o meio ambiente.

Solução? Ainda não

Colocar $phrase fora das aspas simples parece uma boa ideia. Isso funcionará em alguns casos. A abordagem mais ingênua:

find … -exec sh -c 'pdftotext "{}" - | grep --with-filename --label="{}" --color "'$phrase'"' \;

É falho. Com a frase sendo " ; -exec rm "{} , esses são argumentos que nosso find verá:

find
…
-exec
sh
-c
pdftotext "{}" - | grep --with-filename --label="{}" --color ""
;
-exec
rm
"{}"
;

Seus PDFs sumiram. Exemplo artificial? Talvez. Mesmo se você for o único usando o script, essa vulnerabilidade de injeção de código não é nada boa.

Isso foi porque $phrase não foi citado. Você provavelmente sabe que quase sempre deve colocar variáveis entre aspas duplas. Vamos fazer isso. Uma abordagem melhorada:

find … -exec sh -c 'pdftotext "{}" - | grep --with-filename --label="{}" --color "'"$phrase"'"' \;

Com a frase sendo " ; -exec rm "{} this find verá:

find
…
-exec
sh
-c
pdftotext "{}" - | grep --with-filename --label="{}" --color "" ; -exec rm "{}"
;

Parece um pouco melhor; ainda com falhas, porque para foo.pdf sh tentará executar:

pdftotext "foo.pdf" - | grep --with-filename --label="foo.pdf" --color "" ; -exec rm "foo.pdf"

A última parte provavelmente lançará um erro porque não há nenhum comando -exec . E se a frase fosse " ; rm "{} ? E se fosse " ; rm -rf ~/" .

Existe mais. Deixe a frase ser volym (bastante seguro) mas nomeie um dos seus PDFs "; rm -rf ~ #.pdf (isso é possível em alguns sistemas de arquivos, incluindo ext família). Depois que {} -s for substituído, sh executará algo assim:

pdftotext "/home/ad0x/…/"; rm -rf ~ #.pdf" - | grep …

Eu acho que pdftotext irá falhar (irrelevante); então seus arquivos sumiram; então # começa um comentário, qualquer que seja.

Solução

Este é o caminho certo para passar seu {} e $phrase para sh com segurança :

find … -exec sh -c 'pdftotext "$1" - | grep --with-filename --label="$1" --color "$2"' dummy {} "$phrase" \;

Quando esse sh executar a sequência de comandos especificada, $1 será expandido para qualquer find substituído por {} , $2 será expandido para qualquer que seja o shell original substituído por $phrase . No contexto de sh , esses parâmetros são citados corretamente, portanto, você não pode mais injetar código. ( Esta outra resposta minha explica dummy ).

Mesmo agora, há espaço para melhorias. E se a frase fosse -f ? A parte grep acabaria sendo:

grep --with-filename --label="…" --color "-f"

reclamaria do argumento perdido. Use -- para indicar o final das opções; -f após -- não será tratado como uma opção. O mesmo se aplica a pdftotext (embora no seu caso particular todo caminho para PDF deva começar com /home , então ele não pode ser interpretado como uma opção; mas em geral, $1 pode se expandir para uma string que parece uma opção). Nossa invocação sh já está imune porque sh toma opções antes de uma cadeia de comandos e nossa cadeia de comando não pode ser confundida com uma opção (ainda sh -c -- 'pdftotext …' … não causará nenhum dano). Comando mais robusto:

find … -exec sh -c 'pdftotext -- "$1" - | grep --with-filename --label="$1" --color -- "$2"' dummy {} "$phrase" \;
    
por 10.10.2018 / 01:21