Команда для отображения последней директории со специальным файлом - PullRequest
0 голосов
/ 07 февраля 2020

У меня есть каталог с такой структурой:

Fruitables
├── Fruits
│   ├── Grapes
│   │   └── Muscat Grape
│   │       ├── Moscato Giallo
│   │       │   └── information.txt
│   │       └── Muscat Ottonel
│   │           └── information.txt
│   └── Mangoes
│       ├── Ataulfo Mango
│       │   └── information.txt
│       └── Tommy Atkins Mango
│           └── information.txt
└── Vegetables
    └── Potatoes
        ├── Ratte Potato
        │   └── information.txt
        └── Yukon Gold Potato
            └── information.txt

В некоторых файлах information.txt есть строка: «Специальный фрукт или овощ». Мне нужно отобразить каталог, содержащий файл information.txt с этой строкой

Я пробовал это:

shopt -s extglob
shopt -s globstar
grep -H 'Special Fruit or Vegetable' @(Fruitables)/**/*.txt | cut -d: -f1

Это вывод:

Fruitables/Fruits/Grapes/Muscat Grape/Moscato Giallo/information.txt
Fruitables/Fruits/Mangoes/Ataulfo Mango/information.txt
Fruitables/Vegetables/Potatoes/Ratte Potato/information.txt

Я хочу отобразить только последний каталог, как это:

Moscato Giallo
Ataulfo Mango
Ratte Potato

Ответы [ 4 ]

0 голосов
/ 18 февраля 2020

Не совместим с POSIX, но немного безопаснее для имен файлов:

grep -rlZ 'Special Fruit or Vegetable' --include 'information.txt' ./ |  
  xargs -0 dirname -z | 
  xargs -0 -n1 basename
  • -r рекурсивно искать во всех каталогах
  • -l возвращать имена файлов с соответствием вместо совпадающие строки
  • -Z отдельные имена файлов с нулем вместо новой строки /:
  • --include 'information.txt' - поиск только файлов с именем information.txt
  • xargs -0 dirname -z - запустить dirname с найденными файлами выведите ноль после каждого dirname (-z)
  • xargs -0 -n1 basename - запустите basename с одним аргументом (-n1) столько раз, сколько необходимо.

POSIX вариант:

find ./ -name 'information.txt' \
    -exec grep -q 'Special Fruit or Vegetable' {} \; 
    -exec sh -c 'basename "$(dirname "{}")"' \;
  • -name 'information.txt' - поиск файлов с именем information.txt
  • -exec grep -q 'Special Fruit or Vegetable' {} \; - они совпадают? -q говорит grep, что нужно просто установить код завершения, если найдено совпадение
  • -exec sh -c 'basename "$(dirname "{}")"' \; - использует сценарий оболочки для извлечения базового имени dirname файла.
0 голосов
/ 07 февраля 2020

Использование find и grep:

find Fruitables -type f -name '*.txt' -exec sh -c '
  for path; do
    if grep -q "Special Fruit or Vegetable" "$path"; then
      dirname=${path%/*}             # strip filename suffix
      printf "%s\n" "${dirname##*/}" # strip path prefix and print it
    fi
  done
' sh {} +
0 голосов
/ 08 февраля 2020

Вы почти у цели. Как насчет использования awk для последнего прикосновения:

shopt -s globstar
grep -H 'Special Fruit or Vegetable' Fruitables/**/*.txt | cut -d: -f1 | awk -F/ '{f=NF-1; print $f}'

Вывод:

Ratte Potato
Moscato Giallo
Ataulfo Mango

Команда awk извлекает второе поле из последнего, разделяя строку с sla sh.

0 голосов
/ 07 февраля 2020

С учетом того, что вы уже сделали, вы можете поместить этот вывод в переменную bash следующим образом:

pathlist="$(grep -H 'Special Fruit or Vegetable' @(Fruitables)/**/*.txt | cut -d: -f1)"

Затем пропустите этот файл через while read l oop и используйте комбинация basename и dirname:

while read -r path; do
    basename "$(dirname "$path")"
done <<< "pathlist"

Комбинация basename + dirname работает следующим образом:

dirname foo/bar/baz # foo/bar/
basename foo/bar/   # bar

В зависимости от того, что вы пытаетесь сделать с этим выводом, может быть легче поместить это l oop в функцию.

Edit

Если подумать, кажется, вы действительно можете получить вывод на конце л oop. Так что последняя строка l oop может быть done <<< "pathlist" > out.txt

...