Почему использование dirname в команде find дает точки для каждого совпадения? - PullRequest
31 голосов
/ 12 марта 2010

Я использую поиск для задачи, и я заметил, что когда я делаю что-то вроде этого:

find `pwd` -name "file.ext" -exec echo $(dirname {}) \;

это даст вам точки только для каждого матча. Когда вы замените dirname на basename в этой команде, вы получите полный путь. Я что-то напортачу или это ожидаемое поведение? Я привык basename давать вам имя файла (в данном случае file.ext) и dirname давать вам остаток пути.

Ответы [ 7 ]

28 голосов
/ 12 марта 2010

Рассмотрим следующий скрипт:

#!/bin/sh
set -x
find `pwd` -name "file.ext" -exec echo $(dirname {}) \;

set -x показывает, как работает расширение и какова последняя команда. При запуске выдает следующий вывод:

++ pwd
++ dirname '{}'
+ find /home/kibab -name file.ext -exec echo . ';'

Итак, первое, что раскрывается, это pwd. Второй $(dirname {}). Результат этих двух команд затем помещается в команду поиска. Таким образом, вы указываете find на -exec echo ., поэтому вы видите ожидаемый результат.

Когда вы заменяете basename на dirname, расширение по-прежнему имеет место, но результаты расширения отличаются:

  1. pwd расширен до текущего пути. В моем примере выше результат равен /home/kibab
  2. basename {} выполнено. Результат этой команды {}.
  3. Команда поиска выполняется с указанными выше подстановками. Последняя выполненная команда выглядит следующим образом:

    find /home/kibab -name '*.png' -exec echo '{}' ';'

После проверки вышеуказанной команды вы заметите, что команда теперь просто отображает любой найденный файл.

Возможно, вы хотите что-то подобное?

find `pwd` -name "file.ext" -printf "%f\n"
24 голосов
/ 22 июня 2012

Итак, проблема в том, что $ (...) или `...` запускает новую оболочку перед выполнением замены.

Рассмотрите возможность использования bash -c:

$ find . -name '*.PNG' -exec bash -c 'git mv {} $(dirname {})/$(basename {} .PNG)48.png' \;

Переименовывает любой значок в git-репо в более стандартную форму.

Здесь {} заменяется перед выполнением чего-либо, поэтому проблема исчезла.

Для этого примера, TMTOWTDI, но я стараюсь сделать его простым, чтобы вы могли начать все, что вам действительно нужно сделать.

8 голосов
/ 12 марта 2010

вам не нужно вызывать dirname () для каждого найденного файла. с помощью GNU find вы можете использовать -printf и быстрее, таким образом

find /path -type f -iname "*.ext" -printf "%h\n"
6 голосов
/ 12 марта 2010

$(dirname {}) оценивается оболочкой перед передачей в find. Результат этой оценки ., поэтому вы просто указываете find выполнить echo . для каждого найденного файла.

basename {} оценивается как {}, поэтому с $(basename {}) вместо $(dirname {}), find выполнит echo {} для каждого файла. В результате отображается полное имя каждого файла.

Если вы хотите вывести результат dirname для каждого найденного файла, вы можете опустить echo:

find `pwd` -name "file.ext" -exec dirname {} \;
5 голосов
/ 15 декабря 2015

Он показывает вам точки, потому что подстановка процесса оценивается до фактического выполнения команды. Поэтому вы должны передать свою команду в отдельный экземпляр оболочки.

Что касается обходного пути, используйте следующий синтаксис:

find $PWD -name "file.ext" -exec sh -c 'echo $(dirname {})' ';'

Однако самый простой способ напечатать или выполнить что-либо в каждом каталоге - -execdir, например:

find . -name "file.ext" -execdir pwd ';'
4 голосов
/ 12 марта 2010

Я не знаю, почему вы это получаете, но попробуйте это:

find `pwd` -name file.ext |xargs -l1 dirname
0 голосов
/ 12 марта 2010

Это потому, что find печатает пути относительно пути, по которому он ищет. Если вы попробуете этот поиск из /, вы получите `` pwd \ для каждого пути.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...