Коли я 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), дозволяють виконувати заміну процесів , що дозволяє запускати команду та перенаправлятись в / з одного з його потоків. Ви можете перенаправити findstderr grepкоманди на команду, яка її фільтрує, і перенаправити grepstdout цієї команди на stderr.
find args 2> >(grep -Fv 'Permission denied' >&2)
Заслуга в Android Dev для цієї ідеї.
1>&2. Я використав >&2, що рівнозначно ( пов'язано ). Використовуйте те, що вам подобається.Хоча bash підтримує процес заміщення, shв Ubuntu є dash, що не робить. Це дасть вам "Помилка синтаксису: переадресація несподівана", якщо ви спробуєте скористатися цим методом, тоді як метод заміни stdout та stderr все ще працюватиме. Крім того, при bashзапуску в режимі POSIX підтримка заміни процесу відключена.
Одна з ситуацій, коли bashпрацює в режимі POSIX, - це коли його викликають як sh1 . Тому в ОС на зразок 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