Когда пустая строка обозначает текущий каталог?

8

В скрипте я использую, findчтобы собрать некоторые файлы в текущем каталоге, как в

$ find . -name "*.h"
./foo.h

Теперь я бы хотел, чтобы он просто выводил foo.hбез ./префикса. Я думал, что пустая строка ""обозначает текущий каталог в командах оболочки. Но это дает:

$ find "" -name "*.h"
find: ftsopen: No such file or directory

Так что я был не прав. Теперь мой вопрос: когда / как / где / .. означает ли "пустая строка (?)" Текущий каталог в командах, которые ожидают имя файла или путь? Есть ли аккуратное и поучительное объяснение?

Дополнительный вопрос заключается в том, можно ли решить обнаруженную выше ничтожность выше, без манипулирования строками а-ля ${parameter#word}или cutили sed?

PHS
источник
1
findимеет параметр -printf для управления отображением результатов. find . -name '*.h' -printf '%P\n'удалит ./префикс Посмотрите, что find . -name '*.h' -printделает.
Валентин Байрами
Спасибо! Жаль, что моя версия find не поддерживает эту -printfопцию. FWIW, stringsна мою находку дает @(#)PROGRAM:find PROJECT:shell_cmds-175?!?
тел.
5
@phs - это то, что делает обязательным упоминание вашей ОС.
Тердон

Ответы:

11

Давным-давно (в 7-м издании , 32V , 4.2BSD , 4.3BSD ) на уровне системных вызовов имя пути нулевой длины обозначало текущий рабочий каталог (при использовании для поиска; оно было запрещено при попытке создать или удалить файл или каталог). В System III было ошибкой использовать путь нулевой длины при любых обстоятельствах, и стандарт POSIX имеет следующее, что можно сказать о разрешении пути:

Нулевой путь не может быть успешно разрешен.

Марк Плотник
источник
3
Пустая строка в $ PATH, $ MANPATH, $ LD_LIBRARY_PATH (и другие, но не обязательно все $ * PATH) обозначает текущий каталог. dir/в основном такой же, какdir/.
Стефан Шазелас
4

Ты можешь использовать:

find * -name "*.h"

Обратите внимание, что файлы в текущем каталоге, чье имя начинается с, .будут опущены, а файлы, чье имя начинается с, -будут интерпретироваться как опции findи вызывать хаос, поэтому это не является общим эквивалентом find . ….

Отсутствие строки, заканчивающейся на " /" как части имени файла, подразумевает текущий каталог, но это не означает, что текущий каталог обозначается пустой строкой (что, возможно, не совпадает с отсутствием строки, хотя это может выглядеть одинаково при печати).

Энтон
источник
4

Как правило, пустая строка не обозначает текущий каталог, ни для команд оболочки, ни в системных вызовах. Это было на некоторых старых системах, но не на POSIX-совместимых системах .

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

Лучше всего было бы оставить ./. Это не приносит никакого вреда.

Если список файлов предназначен для

find . … | sed 's!^\./!!'

Обратите внимание, что это искажает некоторые имена файлов, содержащие переводы строк. Обычно это не проблема для потребления человеком, и вывод findне подходит для потребления программой, поскольку он неоднозначен. Если вы используете -print0, который подходит для использования в программе, вам, вероятно, все равно не нужен ./префикс.

Вы можете использовать find * …вместо find . …, но обратите внимание, что find *есть ряд дефектов, которые делают его непригодным в целом:

  • . опущен
  • Все точечные файлы (файлы, имя которых начинается с .или ..`) опущены.
  • Если в текущем каталоге есть имя файла, имя которого начинается с -(или файл с именем !или (...), оно будет интерпретировано как опция или предикат find.

Первый пункт не имеет значения, исключает ли ваш фильтр текущий каталог. Во-вторых, вы можете использовать шаблоны ..?* .[!.]* *для сопоставления всех файлов в текущем каталоге, но вам нужно проверить, соответствует ли каждый шаблон хотя бы одному файлу, и пропустить его, если это не так. Это возможно, но очень громоздко. Последний пункт - пробка. Так что find *может подойти для быстрого использования командной строки, но не используйте его в скрипте.

Альтернативный подход состоит в том, чтобы использовать рекурсивное средство оболочки, например

printf '%s\n' **/*.h

Это должно быть активировано с помощью shopt -s globstarbash и set -o globstarksh93, и не существует в базовой оболочке POSIX, такой как dash. Точечные файлы не будут проходить по умолчанию; чтобы включить их, сначала сделайте globbing, а не игнорируйте точечные файлы shopt -s dotglobв bash или FIGNORE='@(.|..)'в ksh93. Также, если совпадений нет, эта команда печатает шаблон; запустите shopt -s nullglobbash, чтобы вместо этого напечатать пустую строку, и используйте шаблон ~(N)**/*.hв ksh.

В zsh рекурсивное сглаживание включено по умолчанию. Используйте спецификатор glob, Dчтобы включить точечные файлы и Nнапечатать пустую строку, если совпадений нет (по умолчанию zsh выдает ошибку, если шаблон не соответствует ни одному файлу). Вы можете использовать printfкак выше или

print -rl -- **/*.h(DN)
Жиль "ТАК - перестань быть злым"
источник
Спасибо за все эти советы. Большинство дефектов с find *новы для меня, а некоторые пугают!
тел .:
2

Я не могу вспомнить ни одного примера, где пустая строка обозначает текущий каталог .. Вы можете думать о таких вызовах, как ls, но это потому, что lsпредполагается, что текущий каталог, если не задан параметр, и на самом деле он не будет принимать пустую строку:

ulmi@silberfisch:~$ ls ""
ls: cannot access : No such file or directory
Ульрих Шварц
источник
3
Один пример, где пустая строка обозначает текущий каталог, является PATHкомпонентом.
августа
0

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

  1. Используйте -printfопцию поиска и скажите, чтобы она только печатала %f. Смотрите man find:

    % f Имя файла с удаленными ведущими каталогами (только последний элемент).

    Например:

    find . -name "*.h" -printf "%f\n"
    
  2. Разобрать вывод:

    find . -name "*.h" | sed 's#^./##'
    
  3. @ Уловка Антона

    find * -name "*.h"
    

Что касается вашего другого вопроса, пустая строка никогда не обозначает текущий каталог. Просто различные программы используют текущий каталог по умолчанию, поэтому, когда вы запускаете их без аргументов, они запускаются в текущем каталоге.

Тердон
источник
Это предполагает GNU find. Или используйте, %Pчтобы только раздеться./
Стефан Шазелас
1
find * -name '*.h'найдете foo/.bar.h, но не .bar.hв текущем каталоге.
Стефан Шазелас
0

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

$ ls *.sh

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

$ find . -name '*.h' -exec basename {} \;

Есть команды, которые получат отсутствие строки в качестве текущего каталога, но в основном потому, что они получат текущий каталог по умолчанию, если ничего не введено. .Всегда обозначают текущую директорию (и ..родительский каталог)

Карл
источник
OP только хотел ./ удален, а ваше findрешение удаляет все компоненты каталога
artm