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" \;
