Хотя ответ, предоставленный @cas, работает в в некоторых случаях он основан на предположении о версии 2.0 epub с документом NCX с именем toc.ncx
на верхнем уровне zip-контейнера. Из 223 файлов epub, которые у меня есть в одной папке, только 5 соответствуют этому предположению - и они содержат его только для совместимости со старыми системами чтения. toc.ncx
не является обязательным файлом - требуется только META-INF / content.xml
. Он будет содержать указатели на все остальные элементы epub. Это делает написание сценариев через bash немного более сложным, но возможным. Вот сценарий, который извлекает название и автора из файла opf (на который указывает content.xml):
#! /bin/sh
for f in "$@" ; do
echo -n "$f"" "
opf=$(unzip -p "$f" META-INF/container.xml |
xml2 |
sed -n -e 's:^/container/rootfiles/rootfile/@full-path=::p')
unzip -p "$f" "$opf" |
xml2 |
sed -n -e 's!^/package/metadata/dc:title=! !p' | tr '
' ' '
unzip -p "$f" "$opf" |
xml2 |
sed -n -e 's!^/package/metadata/dc:creator=! !p' | tr '
' ' '
echo
done
Да, он дважды анализирует opf
, чтобы обеспечить порядок результатов - это создает файл с тремя столбцами, разделенный табуляцией (это табуляции в строках sed между двумя ударами), подходящий для импорта электронной таблицы.
Сделать еще один шаг, чтобы найти файл ncx, немного сложнее, поскольку использование xml2 для генерации одной строки для каждого тега и атрибута работает против нас: нам нужно значение атрибута href
, который является Атрибут media-type
равен application / x-dtbncx + xml
.Мы можем немного обмануть и надеяться, что исходный элемент находится в одной строке, и использовать grep для извлечения только этого фрагмента, а затем обработать его с помощью xml2, чтобы получить значение href.
Поскольку это относительный URL-адрес, нам также необходимо извлечь часть пути из записи opf. Объединение всего этого дает нам:
#! /bin/sh
for f in "$@" ; do
echo "$f"" "
opf=$(unzip -p "$f" META-INF/container.xml |
xml2 |
sed -n -e 's:^/container/rootfiles/rootfile/@full-path=::p')
ncx=$(unzip -p "$f" "$opf" |
grep application/x-dtbncx+xml|
xml2 |
sed -n -e 's!^/item/@href=!!p')
opf_filename=${opf##*/}
opf_path=${opf%$opf_filename}
unzip -p "$f" ${opf_path}${ncx} |
xml2 |
sed -n -e 's:^/ncx/navMap/navPoint/navLabel/text=: :p
s!^/ncx/docTitle/text=!Title: !p'
done
Это все еще делает предположения, самое сильное из которых состоит в том, что это файлы, совместимые с epub2, и, следовательно, где-то содержат файл ncx. В документах Epub3 используется другой формат навигации на основе HTML. Тем не менее, у меня есть оглавления для всех 223 моих тестовых файлов (хотя у некоторых нет заголовков в ncx)
Вот вариант вашей функции findme
, которая выводит результаты, но вместо использования grep
для их нумерации или less
для их страницы перечисляет их из внутреннего массива, а затем предлагает выбрать элемент и программу.
#!/usr/bin/env bash
readarray -O 1 -t results < <(locate -Abi '*\.'"$1" "$2" | grep --color=always -i "$2")
for((i=1; i <= ${#results[*]}; i++))
do
printf "%d: %s\n" $i "${results[i]}"
done
read -p "> " item program
$program "${results[item]}"
Я добавил несколько цитат в ваш исходный скрипт, чтобы лучше справляться с пробелами в именах файлов или даже с параметрами grep. Я скорректировал вызов readarray
так, чтобы результаты начинались с индекса 1 вместо 0, чтобы он соответствовал нумерации grep.
На основе решения, данного Джеффом Шаллером. Я нашел решение, буду рад, если я получу ваши комментарии, чтобы сделать его лучше.
#!/bin/bash
readarray -O 1 -t results < <(locate -Abi '*\.'"$1" "$2")
for((i=1; i <= ${#results[*]}; i++))
do
printf "%d: %s" $i "${results[i]}" | grep --color=always -i "$2"
done
read -p "> " item program
$program "${results[item]}"
Это похоже на простой (в зависимости от сложности темы) ответ.
Но этого недостаточно, если результатов будет больше определенного числа! В этом случае нам нужно что-то вроде больше
или меньше
Обратите внимание, что ваш сценарий findme
имеет несколько проблем:
grep --color = always
дает результаты, которые могут не может использоваться в подстановке команд. Вам нужно, чтобы он проходил через меньше
, но не пытайтесь повторно использовать его в скрипте. grep
для раскрашивания второго аргумента не всегда сработает. Передача -r
в locate
заставляет его использовать регулярные выражения, но с синтаксисом Emacs, который немного отличается от синтаксиса, поддерживаемого grep. В bash вы можете использовать mapfile
, чтобы надежно поместить некоторые строки в массив. Объедините его с подстановкой процесса , чтобы использовать вывод команды. Затем распечатайте этот массив и прочтите ввод пользователя.
findrun () {
mapfile search_hits <(locate -Abir ".*\.$1" "$2")
print '%s\n' "${search_hits[@]}" | grep --color=always -ine "$2"
if read -a cmd; then
set -- "${cmd[@]}"
set -- "$@" "${search_hits[$1]}"
shift
"$@"
fi
}
Альтернативный интерфейс - установка позиционных параметров. Это немного сложно, потому что вы не можете изменить позиционные параметры из функции, но есть обходной способ сделать это в bash , используя псевдоним и источник скрипта. Остерегайтесь цитирования.
alias findrun='. <(echo findrun_prepare \"\$@\"; echo set -- "\"\${search_hits[@]}\"")'
findrun_prepare () {
mapfile search_hits <(locate -Abir ".*\.$1" "$2")
print '%s\n' "${search_hits[@]}" | grep --color=always -ine "$2" >&2
}
Использование:
findrun pdf classifi
evince "$2"