Вы, вероятно, имеете в виду «Отказано find
в доступе » - это то, что в Ubuntu показывает вам, когда вы не можете получить доступ к чему-либо из-за прав доступа к файлу, а не «доступ запрещен».
Одна полностью общая команда, которая делает это правильно (и, в качестве бонуса, переносима на другие * nix- ы, при условии, что сообщение об ошибке совпадает):
(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
(Обычно вы хотите передать некоторые аргументы find
. Они идут до первого перенаправления 3>&1
.)
Однако часто вы сможете использовать что-то более простое. Например, вы, вероятно, можете использовать процесс подстановки . Подробности следуют.
Наиболее распространенные методы и их ограничения
Два типичных подхода - выбросить stderr (как в ответе Zanna ) или перенаправить stderr в stdout и отфильтровать stdout (как в ответе Android Dev ). Хотя они имеют преимущество в простоте написания и зачастую являются разумным выбором, эти подходы не идеальны.
Отбрасывание всего, что отправлено в stderr, например, путем перенаправления его на нулевое устройство с помощью 2>/dev/null
или закрытия его, 2>&-
исключает риск пропуска ошибок, отличных от «Отказано в доступе».
«Отказано в доступе», вероятно, является наиболее распространенной ошибкой, наблюдаемой при запуске find
, но это далеко не единственная возможная ошибка, и если возникает другая, вы можете узнать об этом. В частности, find
сообщает «Нет такого файла или каталога», если отправная точка не существует. С несколькими отправными точками, find
все еще может вернуть некоторые полезные результаты и, кажется, работать. Например, если a
и c
существует, но b
не find a b c -name x
печатает , выдает результат a
, затем «Нет такого файла или каталога» b
, а затем - c
.
Объединение stdout и stderr вместе в stdout и передача его в grep
или к какой-либо другой команде для его фильтрации - как с 2>&1 | grep ...
или - |& grep ...
исключает риск непреднамеренной фильтрации файла, имя которого содержит фильтруемое сообщение.
Например, если вы отфильтровываете строки, содержащие «Отказано в доступе», вы также отбрасываете результаты поиска с именами файлов, такими как «Отказано в доступе messages.txt». Это может произойти случайно, хотя для файла также может быть задано специально созданное имя, чтобы помешать вашему поиску.
Фильтрация объединенных потоков имеет еще одну проблему, которая не может быть уменьшена путем более избирательной фильтрации (например, с grep -vx 'find: .*: Permission denied'
правой стороны канала). Некоторые find
действия, включая -print
неявное действие, когда вы не указываете никаких действий, определяют, как выводить имена файлов на основе того, является ли stdout терминалом.
- Если это не терминал, то имена файлов выводятся как есть, даже если они содержат странные символы, такие как символы новой строки и управляющие символы, которые могут изменить поведение вашего терминала. Если это терминал, то эти символы подавляются и
?
печатаются вместо этого.
- Обычно это то, что вы хотите. Если вы собираетесь обрабатывать имена файлов дальше, они должны выводиться буквально. Однако, если вы собираетесь их отображать, в противном случае имя файла с новой строкой может имитировать несколько имен файлов, а имя файла с последовательностью символов возврата может быть другим именем. Возможны и другие проблемы, такие как имена файлов, содержащие escape-последовательности, которые меняют цвета в вашем терминале.
- Но передача результатов поиска с помощью другой команды (например
grep
) приводит find
к тому, что терминал больше не видит. (Точнее, это приводит к тому, что его стандартный вывод не является терминалом.) Тогда странные символы выводятся буквально. Но если вся команда на правой стороне канала выполняет (а) удаление строк, которые выглядят как сообщения «Отказано в доступе», и (б) выводит то, что осталось, то вы все еще подвержены подобным махинациям, которые являются find
терминальными. обнаружение предназначено для предотвращения.
- Смотрите раздел НЕОБЫЧНЫЕ ФИЛЬМЫ
man find
для получения дополнительной информации, в том числе о поведении каждого из действий, которые печатают имена файлов. ( «Многие из действий find приводят к печати данных, которые находятся под контролем других пользователей ...» ) См. Также разделы 3.3.2.1 , 3.3.2.2 и 3.3.2.3 справочного руководства по GNU Findutils .
Вышеупомянутое обсуждение необычных имен файлов относится к GNU find , который является find
реализацией в системах GNU / Linux, включая Ubuntu.
Оставить один стандартный вывод при фильтрации стандартной ошибки
То , что вы действительно хотите здесь , чтобы оставить стандартный вывод нетронутым , а обжигающе STDERR к grep
. К сожалению, для этого нет простого синтаксиса. |
pipe stdout и некоторые оболочки (в том числе bash
) поддерживают |&
передачу обоих потоков - или вы можете сначала перенаправить stderr на stdout 2>&1 |
, что дает тот же эффект. Но обычно используемые оболочки не предоставляют синтаксис только для pipe stderr.
Вы все еще можете сделать это. Это просто неловко. Один из способов - поменять стандартный вывод с помощью stderr , чтобы результаты поиска находились в stderr, а ошибки - в stdout, а затем направить stdout grep
для фильтрации:
find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
Обычно вы передаете аргументы find
, такие как начальные точки (места для поиска, которые обычно являются каталогами) и предикаты (тесты и действия). Они идут вместо args
выше.
Это работает путем введения нового файлового дескриптора для хранения одного из двух стандартных потоков, которые вы хотите поменять, выполнения перенаправлений для их замены и закрытия нового файлового дескриптора.
- Файловым дескриптором 1 является stdout, а 2 - stderr (а ненаправленный 0 - stdin ). Но вы также можете перенаправить, используя другие файловые дескрипторы. Это может быть использовано, чтобы открыть или оставить открытым файл или устройство.
3>&1
перенаправляет файловый дескриптор 3 на стандартный вывод, так что при последующем перенаправлении стандартного вывода (дескриптор файла 1) исходный стандартный вывод все еще может быть легко записан.
1>&2
перенаправляет стандартный вывод в стандартный поток ошибок Поскольку файловый дескриптор 3 по-прежнему является исходным стандартным выводом, к нему можно получить доступ.
2>&3
перенаправляет stderr в файловый дескриптор 3, который является исходным stdout.
3>&-
закрывает файловый дескриптор 3, который больше не нужен.
- Для получения дополнительной информации см. Как передать stderr, а не stdout? и перенаправление ввода-вывода - обмен stdout и stderr (Advanced) и особенно труба только stderr через фильтр .
Однако этот метод имеет тот недостаток, что результаты поиска отправляются в stderr, а ошибки отправляются в stdout . Если вы запускаете эту команду непосредственно в интерактивной оболочке, а не отправляете и не перенаправляете вывод, это не имеет значения. В противном случае это может быть проблемой. Если вы поместите эту команду в сценарий, а затем кто-то (возможно, вы позже) перенаправит или передаст ее вывод, она не будет работать так, как ожидалось .
Решение состоит в том, чтобы поменять потоки обратно после того, как вы закончите фильтровать вывод . Применение тех же перенаправлений, показанных выше на правой стороне конвейера, не достигнет этого, потому что |
только канал stdout, так что эта сторона конвейера получает только выходные данные, которые были первоначально отправлены в stderr (потому что потоки были поменяны местами), а не оригинал стандартный вывод. Вместо этого вы можете использовать (
)
для запуска вышеупомянутой команды в подоболочке ( связанной ), а затем применить перенаправления перестановки к этому:
(find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
Это группировка, а не конкретная оболочка, которая делает эту работу. Если вы предпочитаете, вы можете использовать {
;}
:
{ find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'; } 3>&1 1>&2 2>&3 3>&-
Менее громоздкий путь: замена процесса
Некоторые оболочки, включая Bash в системах, которые могут его поддерживать (включая системы GNU / Linux, такие как Ubuntu), позволяют выполнять подстановку процессов , что позволяет вам запускать команду и перенаправлять в / из одного из его потоков. Вы можете перенаправить find
stderr grep
команды в команду, которая ее фильтрует, и перенаправить стандартный grep
вывод этой команды в stderr.
find args 2> >(grep -Fv 'Permission denied' >&2)
Заслуга в Android Dev для этой идеи.
Хотя bash
поддерживает подстановку процессов, sh
в Ubuntu есть dash
, чего нет. Если вы попытаетесь использовать этот метод, он выдаст «Синтаксическая ошибка: неожиданное перенаправление», в то время как метод обмена stdout и stderr по-прежнему будет работать. Кроме того, при работе bash
в режиме POSIX поддержка подстановки процессов отключена.
Одна ситуация, когда bash
работает в режиме POSIX, это когда он вызывается как sh
1 . Таким образом, в такой операционной системе, как Fedora, где она bash
предоставляется /bin/sh
, или если вы /bin/sh
указали символическую ссылку на bash
себя в Ubuntu, подстановка процесса по-прежнему не работает в sh
сценарии без предварительной команды для отключения режима POSIX. Лучше всего, если вы хотите использовать этот метод в сценарии, это поставить #!/bin/bash
сверху вместо #!/bin/sh
, если вы этого еще не сделали .
1 : В этой ситуации bash
автоматически включается режим POSIX после запуска команд в сценариях запуска.
Пример
Полезно иметь возможность проверить эти команды. Для этого я создаю tmp
подкаталог текущего каталога и заполняю его некоторыми файлами и каталогами, забирая разрешения у одного из них, чтобы вызвать ошибку «Отказано в доступе» find
.
mkdir tmp; cd tmp; mkdir a b c; touch w a/x 'a/Permission denied messages.txt' b/y c/z; chmod 0 b
Один из каталогов, является доступным включает в себя файл с «Отказано в доступе» в его названии. Бег find
, без переадресаций или трубы показывает этот файл, но и показывает фактическое «Отказано в доступе» ошибка для другого каталога, не доступен:
ek@Io:~/tmp$ find
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘./b’: Permission denied
Передача как stdout, так и stderr grep
и фильтрация строк, содержащих «Permission denied», приводит к исчезновению сообщения об ошибке, но также скрывает результаты поиска файла с этой фразой в названии:
ek@Io:~/tmp$ find |& grep -Fv 'Permission denied'
.
./a
./a/x
./c
./c/z
./w
./b
find 2>&1 | grep -Fv 'Permission denied'
эквивалентно и дает тот же результат.
Методы, показанные выше для фильтрации «Отказано в доступе» только из сообщений об ошибках, а не из результатов поиска, являются успешными. Например, вот метод, в котором stdout и stderr меняются местами:
ek@Io:~/tmp$ (find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find args 2> >(grep -Fv 'Permission denied' >&2)
производит тот же результат.
Вы можете вызвать другое сообщение об ошибке, чтобы гарантировать, что строки, отправленные в stderr, которые не содержат текст «Отказано в доступе», по-прежнему пропускаются. Например, здесь я запустил find
текущий каталог ( .
) в качестве одной отправной точки, а несуществующий каталог - в foo
качестве другой:
ek@Io:~/tmp$ (find . foo 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘foo’: No such file or directory
Проверка того, что find
стандартный вывод все еще является терминалом
Мы также можем видеть, какие команды вызывают буквальное отображение специальных символов, таких как символы новой строки. (Это можно сделать отдельно от демонстрации выше, и это не обязательно должно быть в tmp
каталоге.)
Создайте файл с новой строкой в названии:
touch $'abc\ndef'
Обычно в качестве отправной точки мы используем каталоги find
, но файлы тоже работают:
$ find abc*
abc?def
Передача stdout другой команде приводит к буквальному выводу новой строки, создавая ложное впечатление о двух отдельных результатах поиска abc
и def
. Мы можем проверить это с cat
:
$ find abc* | cat
abc
def
Перенаправление просто stderr не вызывает этой проблемы:
$ find abc* 2>/dev/null
abc?def
Не закрывая его:
$ find abc* 2>&-
abc?def
Трубопроводы для grep
действительно причина проблемы:
$ find abc* |& grep -Fv 'Permission denied'
abc
def
(Замена |&
на 2>&1 |
эквивалентную и дает тот же результат.)
Замена stdout и stderr и stdout по конвейеру не вызывает проблемы find
- stdout становится stderr, который не передается по конвейеру:
$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def
Группировка этой команды и замена потоков обратно не вызывает проблемы:
$ (find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
abc?def
( {
;}
Версия выдает тот же результат.)
Использование подстановки процесса для фильтрации stderr также не вызывает проблемы:
$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def