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

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

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

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

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

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

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

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

решение1

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

Пустое имя пути не может быть успешно разрешено.

решение2

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

find * -name "*.h"

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

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

решение3

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

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

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

Если список файлов для

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

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

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

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

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

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

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

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

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

print -rl -- **/*.h(DN)

решение4

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

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

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

    Например:

    find . -name "*.h" -printf "%f\n"
    
  2. Анализ вывода:

    find . -name "*.h" | sed 's#^./##'
    
  3. @Трюк Энтона

    find * -name "*.h"
    

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

Связанный контент