Как я могу выбрать один из результатов `locate` и позволить открыть его с помощью указанного приложения

Хотя ответ, предоставленный @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)

1
03.11.2016, 01:57
3 ответа

Вот вариант вашей функции 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.

2
27.01.2020, 23:19

На основе решения, данного Джеффом Шаллером. Я нашел решение, буду рад, если я получу ваши комментарии, чтобы сделать его лучше.

#!/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]}"

Это похоже на простой (в зависимости от сложности темы) ответ.

Но этого недостаточно, если результатов будет больше определенного числа! В этом случае нам нужно что-то вроде больше или меньше

0
27.01.2020, 23:19

Обратите внимание, что ваш сценарий findme имеет несколько проблем:

  • Отсутствие двойных кавычек вокруг подстановок переменных
  • Результат grep --color = always дает результаты, которые могут не может использоваться в подстановке команд. Вам нужно, чтобы он проходил через меньше , но не пытайтесь повторно использовать его в скрипте.
  • grep и locate используют другой синтаксис шаблона, поэтому использование 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"
2
27.01.2020, 23:19

Теги

Похожие вопросы