В скрипте, который я использую 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 без начального символа ./
:
Используйте опцию find
-printf
и укажите ему печатать только%f
. Смотритеman find
:%f Имя файла, из которого удалены все начальные каталоги (только последний элемент).
Например:
find . -name "*.h" -printf "%f\n"
Анализ вывода:
find . -name "*.h" | sed 's#^./##'
-
find * -name "*.h"
Что касается вашего другого вопроса, пустая строка никогда не обозначает текущий каталог. Просто различные программы принимают текущий каталог по умолчанию, поэтому, когда вы запускаете их без аргументов, они запускаются в текущем каталоге.