Коли я find
бачу всі файли pdf у /home
каталозі, я бачу access denied
. Щоб усунути їх, я спробував:
find /home -iname "*.pdf" | grep -v "access denied"
Однак результат той самий. Як я можу позбутися цих рядків?
Коли я find
бачу всі файли pdf у /home
каталозі, я бачу access denied
. Щоб усунути їх, я спробував:
find /home -iname "*.pdf" | grep -v "access denied"
Однак результат той самий. Як я можу позбутися цих рядків?
Відповіді:
Те, що ви спробували, не спрацювало, тому що access denied
вихідні дані є помилками та надсилаються на STDERR замість STDOUT, на який покладено grep
.
Ви можете уникнути бачення цих помилок, перенаправляючи лише STDERR
find /home -iname "*.pdf" 2>/dev/null
Або, як коментує Девід Фоерстер, ми можемо більш коротко закрити STDERR
find /home -iname "*.pdf" 2>&-
Однак, я підозрюю, що ви насправді хочете шукати лише ваш будинок, а не інших користувачів, тому, можливо, ви цього дуже хочете
find ~ -iname "*.pdf"
Якщо це призводить до помилок, у вашому локальному конфігурації можуть виникнути неправильні права власності, які слід дослідити.
sudo chown $USER: ~/.gvfs ~/.dbus
2>&-
. Пошук GNU не закінчиться, якщо він спробує записати повідомлення про помилки до дескриптора файлу. Тому що питання власності sudo chown -R $USER: ...
були б більш ефективними у випадку, якщо більше файлів, які не належать $USER
.
Заборонений доступ, ймовірно, надруковано, stderr
а не stdout
.
Спробуйте це:
find /home -iname "*.pdf" 2>&1 | grep -v "access denied"
Вихід 2>&1
перенаправляє з stderr
на stdout
, щоб він grep -v
міг виконувати свою роботу. (За замовчуванням, |
лише труби stdout
та не stderr
.)
2>&1
... Я не баш експерт, тому, якщо це неправильно, то, будь ласка, скажіть так :)
bash
в Ubuntu він є, за винятком режиму POSIX . Я думаю, що це найкраще рішення - файл із зловмисними назвами access denied
все одно з’явиться.
Ви, мабуть, маєте на увазі "Відмовлено у дозволі" - те, що find
в Ubuntu показує вам, коли ви не можете отримати доступ до чогось із-за дозволів на файли, а не "відмовлено у доступі".
Одна повністю загальна команда, яка робить це правильно (і, як бонус, переноситься для інших * nix es, якщо повідомлення про помилку є однаковим):
(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
(Зазвичай ви хочете передати деякі аргументи find
. Вони йдуть до першого перенаправлення 3>&1
.)
Однак часто вам вдасться використовувати щось простіше. Наприклад, ви, ймовірно, можете використовувати підстановку процесу . Деталі випливають.
Два типових підходу - це викинути stderr (як у відповіді Занні ) або перенаправити 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 ...
створює ризик ненавмисної фільтрації файлу, ім'я якого містить повідомлення, яке фільтрується.
Наприклад, якщо ви фільтруєте рядки, які містять "Дозвіл відхилено", ви також видалите результати пошуку із зазначенням назви файлів, наприклад "Дозволу відхилено повідомлення.txt". Це, мабуть, трапиться випадково, хоча також файлу можна буде присвоїти спеціально створене ім’я, яке б перешкодило вашим пошукам.
Фільтрація комбінованих потоків має ще одну проблему, яку не можна пом'якшити, фільтруючи більш вибірково (наприклад, grep -vx 'find: .*: Permission denied'
у правій частині труби). Деякі find
дії, включаючи -print
дію, яка неявна, якщо ви не вказуєте жодної дії, визначають, як вивести назви файлів, виходячи з того, чи ні stdout є терміналом.
?
друкуються замість цього.grep
) змушує find
більше не бачити термінал. (Точніше, це призводить до того, що його stdout не є терміналом.) Тоді дивні символи виводяться буквально. Але якщо вся команда з правого боку труби - це (а) видалення рядків, схожих на повідомлення "Відмовлено у дозволі", та (б) друк того, що залишилося, то ти все ще підпадаєш під тип шенагіганів, що find
є терміналом виявлення призначене для запобігання.man find
інформацію, включаючи поведінку кожної з дій, які друкують назви файлів, див. ( «Багато дій результату пошуку в якому інформацію , яка знаходиться під контролем інших користувачів ...» ) Дивиться також розділи 3.3.2.1 , 3.3.2.2 і 3.3.2.3 в довідковому керівництві GNU Findutils .Вищенаведене обговорення незвичайних імен файлів стосується пошуку GNU , що є find
реалізацією в системах GNU / Linux, включаючи Ubuntu.
Те , що ви дійсно хочете тут , щоб залишити стандартний висновок недоторканим , а пекуче STDERR до grep
. На жаль, для цього немає простого синтаксису. |
трубопровід і деякі оболонки (включаючи bash
) підтримують |&
трубу обох потоків - або ви можете перенаправити stderr на stdout спочатку 2>&1 |
, що має той же ефект. Але часто використовувані оболонки не забезпечують синтаксис лише для трубопроводу stderr.
Ви все ще можете це зробити. Це просто незручно. Один із способів полягає в тому, щоб поміняти stdout на stderr , щоб результати пошуку були на stderr, а помилки були на stdout, а потім stdout grep
для фільтрування:
find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
Зазвичай ви передаватимете аргументи find
, такі як вихідні точки (місця пошуку, які зазвичай є каталогами) та предикати (тести та дії). Вони йдуть замість args
вище.
Це працює за допомогою введення нового дескриптора файлів для утримування одного з двох стандартних потоків, якими ви хочете поміняти місцями, виконуючи переадресації для їх заміни та закриття нового дескриптора файлів.
3>&1
перенаправляє дескриптор файлу 3 на stdout, так що коли згодом stdout (дескриптор файлу 1) буде перенаправлений, вихідний stdout все ще може бути легко записаний.1>&2
перенаправляє stdout на stderr. Оскільки дескриптор файлу 3 все ще є оригінальним stdout, до нього ще можна отримати доступ.2>&3
перенаправляє stderr до дескриптора файлів 3, який є оригінальним stdout.3>&-
закриває дескриптор файлу 3, який більше не потрібен.Однак у цього методу є недолік, що результати пошуку надсилаються до stderr, а помилки надсилаються до 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
stdout цієї команди на stderr.
find args 2> >(grep -Fv 'Permission denied' >&2)
Заслуга в 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
.
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
та відфільтрування рядків, що містять "Дозвіл відхилено", приводить до повідомлення про помилку, але також приховує результат пошуку файлу з такою фразою в його імені:
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 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