Видалення рядків "Доступ заборонено"


9

Відповіді:


19

Те, що ви спробували, не спрацювало, тому що access deniedвихідні дані є помилками та надсилаються на STDERR замість STDOUT, на який покладено grep.

Ви можете уникнути бачення цих помилок, перенаправляючи лише STDERR

find /home -iname "*.pdf" 2>/dev/null

Або, як коментує Девід Фоерстер, ми можемо більш коротко закрити STDERR

find /home -iname "*.pdf" 2>&-

Однак, я підозрюю, що ви насправді хочете шукати лише ваш будинок, а не інших користувачів, тому, можливо, ви цього дуже хочете

find ~ -iname "*.pdf"

Якщо це призводить до помилок, у вашому локальному конфігурації можуть виникнути неправильні права власності, які слід дослідити.


3
Гррр, чому люди завжди б'ють мене за 30 секунд? : \
YouAGitForNotUsingGit

знайти: “/home/ihsan/.gvfs”: доступ заборонено знайти: “/home/ihsan/.dbus”: доступ заборонено, для команди ~
solfish

чи щось не так? так, я також хочу домашній каталог інших користувачів, який також створений мною для тестування
solfish

2
@solfish Наскільки я знаю, ці файли повинні належати вам. Можливо, ви хочетеsudo chown $USER: ~/.gvfs ~/.dbus
Zanna

1
Її має бути достатньо, щоб закрити більш жорсткий 2>&-. Пошук GNU не закінчиться, якщо він спробує записати повідомлення про помилки до дескриптора файлу. Тому що питання власності sudo chown -R $USER: ...були б більш ефективними у випадку, якщо більше файлів, які не належать $USER.
Девід Фоерстер

8

Заборонений доступ, ймовірно, надруковано, stderrа не stdout.

Спробуйте це:

find /home -iname "*.pdf" 2>&1 | grep -v "access denied"

Вихід 2>&1перенаправляє з stderrна stdout, щоб він grep -vміг виконувати свою роботу. (За замовчуванням, |лише труби stdoutта не stderr.)


але для цього 2> & 1 означає, якщо stderr існує відправити в stdout?
солянка

@solfish Yup, саме в цьому і
полягає

що я не розумію, це перед "|" як вихід; у нас просто складніше? і після "|" як вхід ми отримали це
solfish

@solfish Ну, я зіткнувся з цією проблемою близько півтора років тому, і мені вдалося виправити її іншим методом . Але тоді коментар нижче моєї відповіді запропонував просто використовувати 2>&1... Я не баш експерт, тому, якщо це неправильно, то, будь ласка, скажіть так :)
YouAGitForNotUsingGit

@AndroidDev Я пропоную додати цей варіант відповіді як альтернативу. Критика Ітана Рейснера полягала в тому, що заміна процесу не є портативною. Але bashв Ubuntu він є, за винятком режиму POSIX . Я думаю, що це найкраще рішення - файл із зловмисними назвами access deniedвсе одно з’явиться.
Елія Каган

4

Ви, мабуть, маєте на увазі "Відмовлено у дозволі" - те, що 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вище.

Це працює за допомогою введення нового дескриптора файлів для утримування одного з двох стандартних потоків, якими ви хочете поміняти місцями, виконуючи переадресації для їх заміни та закриття нового дескриптора файлів.

  • Дескриптор файлу 1 - stdout, а 2 - stderr (а ненаправлений 0 - stdin ). Але ви також можете перенаправляти, використовуючи інші дескриптори файлів. Це можна використовувати для відкриття або збереження відкритого файлу чи пристрою.
  • 3>&1 перенаправляє дескриптор файлу 3 на stdout, так що коли згодом stdout (дескриптор файлу 1) буде перенаправлений, вихідний stdout все ще може бути легко записаний.
  • 1>&2перенаправляє stdout на stderr. Оскільки дескриптор файлу 3 все ще є оригінальним stdout, до нього ще можна отримати доступ.
  • 2>&3 перенаправляє stderr до дескриптора файлів 3, який є оригінальним stdout.
  • 3>&- закриває дескриптор файлу 3, який більше не потрібен.
  • Для отримання додаткової інформації див. Як прокласти трубу stderr, а не stdout? і перенаправлення IO - заміна stdout і stderr (Advanced) і особливо трубопровід тільки stderr через фільтр .

Однак у цього методу є недолік, що результати пошуку надсилаються до 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 для цієї ідеї.

Хоча 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.