-1

Я использую эти команды для поиска в нескольких PDF-файлах, учитывая путь к файлу:

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

Где phrase - это термин, который вы хотите найти в PDF-файлах. Это работает как ожидалось. Я получаю все вхождения слова "волым". Выход в терминал

Когда я пытаюсь сделать то же самое в сценарии .sh (search.sh)

#!/bin/bash
read -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

Он выводит каждую строку в каждом файле PDF. Выход: выводит каждую строку в каждом PDF

Я подозреваю, что это как-то связано с тем, как read интерпретирует входные данные, но я не нашел решения моей проблемы в Интернете.

1 ответ1

2

Прямой виновник - $phrase в одинарных кавычках. Это не единственная проблема.

Что просходит

Это соответствующий код (обратите внимание, я использую многоточие для наименее интересной части; такая строка предназначена для понимания людьми, а не выполняется непосредственно в оболочке):

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

Оболочка, которая интерпретирует скрипт, содержит значение переменной phrase ; скажем, значение является volym . В приведенной выше команде все, что находится в одинарных кавычках, остается нетронутым, потому что именно так работает одинарное цитирование; так что $phrase еще не расширена. Оболочка только разбирает \ что сообщает ей следующее ; не предназначен для разделения команд, он должен рассматриваться как аргумент командной строки для find .

Когда запускается утилита find , это то, что она видит в качестве аргументов (начиная с 0-го, т. Е. Сама find ; один аргумент в строке, кроме который обозначает несколько менее интересных аргументов):

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

Обратите внимание, что последняя, но одна строка - это один длинный аргумент.

Давайте предположим, что foo.pdf найден и -exec выполнит свою работу. Все аргументы между -exec и ; стать новой командой после замены {} на foo.pdf . Новая команда будет (опять же, начиная с 0-го аргумента; один аргумент в строке):

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

Таким образом, sh запускает, он получает -c и поэтому знает, что следующий аргумент должен быть выполнен так, как если бы он был напечатан в командной строке:

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

В этот момент $phrase расширена. Он расширяется до нуля (последнее слово становится ""), потому что он не был установлен в этой оболочке. Это расширится до volym если вы экспортируете переменную в своем скрипте; но ты не сделал. Я не экспортировал бы все же; по моему мнению, в этом случае экспорт будет излишне загрязнять окружающую среду.

Решение? Еще нет

Ввод $phrase за пределы одинарных кавычек кажется хорошей идеей. Это будет работать в некоторых случаях. Самый наивный подход:

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

Это некорректно. Фраза " ; -exec rm "{} - это аргументы, которые увидит наша find :

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

Ваши PDF-файлы исчезли. Искусственный пример? Может быть. Даже если вы единственный, кто использует скрипт, такая уязвимость внедрения кода ничего хорошего не дает.

Это было потому, что $phrase вообще не цитировали. Вы, вероятно, знаете, что почти всегда следует ставить переменные в двойных кавычках. Давай сделаем это. Улучшенный подход:

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

С фразой " ; -exec rm "{} эта find будет выглядеть так:

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

Выглядит несколько лучше; все еще несовершенен, потому что для foo.pdf sh попытается запустить:

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

Последняя часть, скорее всего, выдаст ошибку, потому что нет команды -exec . Что если фраза была " ; rm "{}? Что если это было " ; rm -rf ~/" .

Есть больше. Пусть фраза будет volym (вполне безопасна), но назовите один из ваших PDF-файлов "; rm -rf ~ #.pdf (это возможно в нескольких файловых системах, включая семейство ext). После замены {} -s sh запустит что-то вроде этого:

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

Я предполагаю, что pdftotext потерпит неудачу (не имеет значения); тогда ваши файлы исчезли; затем # начинает комментарий, что угодно.

Решение

Это правильный путь , чтобы передать ваши {} и $phrase sh безопасно:

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

Когда этот sh выполняет заданную командную строку, $1 расширяется до любой find заменяющей {} , $2 расширяется до любой исходной оболочки, заменяющей $phrase . В контексте sh эти параметры правильно указаны, поэтому вы больше не можете вводить код. (Этот другой мой ответ объясняет dummy).

Даже сейчас есть возможности для улучшения. Что если фраза была -f? Часть grep конечном итоге будет:

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

было бы жаловаться на отсутствующий аргумент. Используйте -- чтобы указать конец опций; -f после -- не будет рассматриваться как вариант. То же самое относится и к pdftotext (хотя в вашем конкретном случае каждый путь к PDF должен начинаться с /home поэтому его нельзя интерпретировать как параметр; но в целом $1 может расшириться до строки, которая выглядит как параметр). Наш вызов sh уже защищен, потому что sh принимает параметры перед командной строкой, и наша командная строка не может быть ошибочно принята за параметр (все же sh -c -- 'pdftotext …' … принесет вреда). Более надежная команда:

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .