Когда я использую, find
чтобы увидеть все файлы PDF в /home
каталоге, я вижу access denied
. Чтобы устранить их, я попытался:
find /home -iname "*.pdf" | grep -v "access denied"
Тем не менее, результат тот же. Как я могу избавиться от этих строк?
command-line
bash
find
solfish
источник
источник
Ответы:
То, что вы пробовали, не сработало, потому что
access denied
выходные данные являются ошибками и отправляются на STDERR вместо STDOUT, который передается по каналуgrep
.Вы можете избежать этих ошибок, перенаправляя только STDERR
Или, как прокомментировал Дэвид Фёрстер, мы можем более кратко закрыть STDERR
Тем не менее, я подозреваю, что вы на самом деле хотите искать свой дом, а не других пользователей, так что, возможно, вы действительно хотите
Если это приводит к ошибкам, в вашей локальной конфигурации могут быть некоторые неправильные владельцы, которые вы должны исследовать.
источник
sudo chown $USER: ~/.gvfs ~/.dbus
2>&-
. GNU find не завершит свою работу, если попытается записать сообщения об ошибках в дисфункциональный файловый дескриптор. Поскольку проблемы собственностиsudo chown -R $USER: ...
были бы более эффективными в случае большего количества файлов в пределах, которые не принадлежат$USER
.Отказано в доступе, вероятно, печатается,
stderr
а неstdout
.Попробуй это:
2>&1
Перенаправляет выводstderr
наstdout
, так чтоgrep -v
может делать свою работу. (По умолчанию|
только трубы,stdout
а неstderr
.)источник
2>&1
... Я не эксперт по bash, поэтому, если это неверно, пожалуйста, скажите так :)bash
в Ubuntu он есть, кроме как в режиме POSIX . Я думаю, что это лучшее решение - файл со злонамеренным именемaccess denied
все равно появится.Вы, вероятно, имеете в виду «Отказано
find
в доступе » - это то, что в Ubuntu показывает вам, когда вы не можете получить доступ к чему-либо из-за прав доступа к файлу, а не «доступ запрещен».Одна полностью общая команда, которая делает это правильно (и, в качестве бонуса, переносима на другие * nix- ы, при условии, что сообщение об ошибке совпадает):
(Обычно вы хотите передать некоторые аргументы
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 терминалом.?
печатаются вместо этого.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 на stdout2>&1 |
, что дает тот же эффект. Но обычно используемые оболочки не предоставляют синтаксис только для pipe stderr.Вы все еще можете сделать это. Это просто неловко. Один из способов - поменять стандартный вывод с помощью stderr , чтобы результаты поиска находились в stderr, а ошибки - в stdout, а затем направить stdout
grep
для фильтрации:Обычно вы передаете аргументы
find
, такие как начальные точки (места для поиска, которые обычно являются каталогами) и предикаты (тесты и действия). Они идут вместоargs
выше.Это работает путем введения нового файлового дескриптора для хранения одного из двух стандартных потоков, которые вы хотите поменять, выполнения перенаправлений для их замены и закрытия нового файлового дескриптора.
3>&1
перенаправляет файловый дескриптор 3 на стандартный вывод, так что при последующем перенаправлении стандартного вывода (дескриптор файла 1) исходный стандартный вывод все еще может быть легко записан.1>&2
перенаправляет стандартный вывод в стандартный поток ошибок Поскольку файловый дескриптор 3 по-прежнему является исходным стандартным выводом, к нему можно получить доступ.2>&3
перенаправляет stderr в файловый дескриптор 3, который является исходным stdout.3>&-
закрывает файловый дескриптор 3, который больше не нужен.Однако этот метод имеет тот недостаток, что результаты поиска отправляются в stderr, а ошибки отправляются в stdout . Если вы запускаете эту команду непосредственно в интерактивной оболочке, а не отправляете и не перенаправляете вывод, это не имеет значения. В противном случае это может быть проблемой. Если вы поместите эту команду в сценарий, а затем кто-то (возможно, вы позже) перенаправит или передаст ее вывод, она не будет работать так, как ожидалось .
Решение состоит в том, чтобы поменять потоки обратно после того, как вы закончите фильтровать вывод . Применение тех же перенаправлений, показанных выше на правой стороне конвейера, не достигнет этого, потому что
|
только канал stdout, так что эта сторона конвейера получает только выходные данные, которые были первоначально отправлены в stderr (потому что потоки были поменяны местами), а не оригинал стандартный вывод. Вместо этого вы можете использовать(
)
для запуска вышеупомянутой команды в подоболочке ( связанной ), а затем применить перенаправления перестановки к этому:Это группировка, а не конкретная оболочка, которая делает эту работу. Если вы предпочитаете, вы можете использовать
{
;}
:Менее громоздкий путь: замена процесса
Некоторые оболочки, включая Bash в системах, которые могут его поддерживать (включая системы GNU / Linux, такие как Ubuntu), позволяют выполнять подстановку процессов , что позволяет вам запускать команду и перенаправлять в / из одного из его потоков. Вы можете перенаправить
find
stderrgrep
команды в команду, которая ее фильтрует, и перенаправить стандартныйgrep
вывод этой команды в stderr.Заслуга в Android Dev для этой идеи.
1>&2
. Я использовал>&2
, что эквивалентно ( связано ). Используйте то, что вам нравится.Хотя
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
.Один из каталогов, является доступным включает в себя файл с «Отказано в доступе» в его названии. Бег
find
, без переадресаций или трубы показывает этот файл, но и показывает фактическое «Отказано в доступе» ошибка для другого каталога, не доступен:Передача как stdout, так и stderr
grep
и фильтрация строк, содержащих «Permission denied», приводит к исчезновению сообщения об ошибке, но также скрывает результаты поиска файла с этой фразой в названии:find 2>&1 | grep -Fv 'Permission denied'
эквивалентно и дает тот же результат.Методы, показанные выше для фильтрации «Отказано в доступе» только из сообщений об ошибках, а не из результатов поиска, являются успешными. Например, вот метод, в котором stdout и stderr меняются местами:
find args 2> >(grep -Fv 'Permission denied' >&2)
производит тот же результат.Вы можете вызвать другое сообщение об ошибке, чтобы гарантировать, что строки, отправленные в stderr, которые не содержат текст «Отказано в доступе», по-прежнему пропускаются. Например, здесь я запустил
find
текущий каталог (.
) в качестве одной отправной точки, а несуществующий каталог - вfoo
качестве другой:Проверка того, что
find
стандартный вывод все еще является терминаломМы также можем видеть, какие команды вызывают буквальное отображение специальных символов, таких как символы новой строки. (Это можно сделать отдельно от демонстрации выше, и это не обязательно должно быть в
tmp
каталоге.)Создайте файл с новой строкой в названии:
Обычно в качестве отправной точки мы используем каталоги
find
, но файлы тоже работают:Передача stdout другой команде приводит к буквальному выводу новой строки, создавая ложное впечатление о двух отдельных результатах поиска
abc
иdef
. Мы можем проверить это сcat
:Перенаправление просто stderr не вызывает этой проблемы:
Не закрывая его:
Трубопроводы для
grep
действительно причина проблемы:(Замена
|&
на2>&1 |
эквивалентную и дает тот же результат.)Замена stdout и stderr и stdout по конвейеру не вызывает проблемы
find
- stdout становится stderr, который не передается по конвейеру:Группировка этой команды и замена потоков обратно не вызывает проблемы:
(
{
;}
Версия выдает тот же результат.)Использование подстановки процесса для фильтрации stderr также не вызывает проблемы:
источник