Як я можу знайти, коли ім'я файлу містить пробіли?


17

Я хочу передавати імена файлів іншим програмам, але всі вони задихаються, коли імена містять пробіли.

Скажімо, у мене є файл з назвою.

foo bar

Як я можу findдомогтися повернення правильного імені?

Очевидно, що я хочу:

foo\ bar

або:

"foo bar"

EDIT : Я не хочу проходити xargs, я хочу отримати правильно відформатований рядок, findщоб я міг передавати рядок імен файлів безпосередньо іншій програмі.


5
до чого ти це робиш? вам відомо про -execпрапор find? ви могли потенційно полегшити цю помилку і зробити свою команду більш ефективною, виконуючи -execзамість того, щоб передати її іншим командам. Просто мій $ .02
h3rrmiller

6
@bug: findформатування назв файлів просто чудово; вони записуються по одному імені на рядок. (Звичайно, це неоднозначно, якщо ім'я файлу містить символ нового рядка.) Отже, проблема полягає в тому, що кінець прийому "задихається", коли він отримує пробіл, а це означає, що ви повинні сказати нам, що це кінець прийому, якщо ви хочете змістовної відповіді. .
rici

2
Те, що ви називаєте "правильно відформатованим", - це справді "уникнене споживання оболонкою". Більшість утиліт, які можуть прочитати купу імен файлів, задушиться ім'ям, що уникнуло оболонки, але насправді має сенс запропонувати (наприклад) findможливість виводу імен файлів у форматі, що підходить для оболонки. В цілому, однак, розширення -print0GNU findчудово працює для багатьох інших сценаріїв (теж), і вам слід навчитися використовувати його в будь-якому випадку.
tripleee

2
@bug: До речі, ls $(command...)список не подається stdin. Він ставить вихід $(command...)безпосередньо в командний рядок. У такому випадку це оболонка, яка $IFSзчитується з c, і вона використовуватиме поточне значення, щоб вирішити, як розбити слово на виводі. Загалом, вам краще використовувати xargs. Ви не помітите хіт ефективності.
rici

2
find -printf '"%p"\n'додасть подвійні лапки навколо кожного знайденого імені, але не буде належним чином цитувати подвійні лапки у назві файлу. Якщо в іменах ваших файлів немає вбудованих подвійних лапок, ви можете проігнорувати проблему: або пропустити sed 's/"/&&/g;s/^""/"/;s/""$/"/'. Якщо імена ваших файлів в кінцевому підсумку обробляються оболонкою, ви, ймовірно, повинні використовувати одинарні лапки замість подвійних лапок, хоча (інакше sweet$HOMEвони стануть чимось на зразок sheet/home/you). І це все ще не дуже надійно щодо імен файлів з новими рядками в них. Як ви хочете впоратися з ними?
tripleee

Відповіді:


18

ВІДКЛЮЧНО:

find . -type f -exec sh -c '
  for f do
    : command "$f"
  done
' sh {} +

З findпідтримками -print0та xargsпідтримками -0:

find . -type f -print0 | xargs -0 <command>

-0 опція повідомляє xargs використовувати символ ASCII NUL замість пробілу, щоб закінчити (розділити) назви файлів.

Приклад:

find . -maxdepth 1 -type f -print0 | xargs -0 ls -l

Не працює. Коли я бігаю, ls $(find . -maxdepth 1 -type f -print0 | xargs -0)я отримую ls: cannot access ./foo: No such file or directory ls: cannot access bar: No such file or directory
помилка

1
Ви пробували це так, як насправді його написав Gnouc? Якщо ви наполягаєте на тому, щоб зробити це так, спробуйте $(..)"$(..)"
укласти

3
@bug: ваша команда неправильна. Спробуйте саме я читаю і читаю сторінку findта xargs.
cuonglm

Я бачу, потім знову хочу отримати відформатований рядок, який я міг би передавати безпосередньо.
помилка

1
@bug: Просто використовуйте xargs -0 <свою програму>
cuonglm

10

Використання -print0- це один варіант, але не всі програми підтримують використання потоків даних, обмежених нульбайтами, тому для деяких речей вам доведеться використовувати xargsцей -0параметр, як відзначається у відповіді Gnouc.

Альтернативою було б використання finds -execабо -execdirопцій. Перший із наведених нижче буде посилати назви файлів по somecommandодному, а другий - до списку файлів:

find . -type f -exec somecommand '{}' \;
find . -type f -exec somecommand '{}' +

Ви можете виявити, що вам краще використовувати глобус у багатьох випадках. Якщо у вас є сучасна оболонка (bash 4+, zsh, ksh), ви можете отримати рекурсивне глобулювання за допомогою globstar( **). У bash, ви повинні встановити це:

shopt -s globstar
somecommand ./**/*.txt ## feeds all *.txt files to somecommand, recursively

У shopt -s globstar extglobмоєму .bashrc є рядок із заговором, тому для мене це завжди ввімкнено (і так є розширені глобуси, які також корисні).

Якщо ви не хочете рекурсивності, очевидно, просто використовуйте ./*.txtнатомість, щоб використовувати кожен * .txt у робочому каталозі. findмає декілька дуже корисних дрібнозернистих можливостей пошуку і є обов'язковим для десятків тисяч файлів (в цей момент ви зіткнетеся з максимальною кількістю аргументів оболонки), але для щоденного використання це часто непотрібно.


Привіт, @evilsoup, що робить {} у цьому сценарії?
Аюсман

3

Особисто я використовую дію -execпошуку, щоб вирішити подібну проблему. Або, якщо потрібно xargs, що дозволяє паралельне виконання.

Однак існує спосіб findстворити нечитабельний список імен файлів. Не дивно, що він використовує -execі bash, зокрема, розширення до printfкоманди:

find ... -exec bash -c 'printf "%q " "$@"' printf {} ';'

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

ls $(find ... -exec bash -c 'printf "%q " "$@"' printf {} +)

Що ви повинні зробити:

eval "ls $(find ... -exec bash -c 'printf "%q " "$@"' printf {} +)"

(Зверніть увагу, що я не робив жодної реальної спроби перевірити вищезгадану чудовисько.)

Але тоді ви також можете зробити:

find ... -exec ls {} +

Я не думаю, що lsсценарій належним чином відображає випадок використання ОП, але це лише спекуляція, оскільки нам не було показано, що саме він намагається виконати. Це рішення насправді працює дуже добре; Я отримую результат, який я (розпливчасто) очікував на всі кумедні імена файлів, які я спробував, в тому числіtouch "$(tr a-z '\001-\026' <<<'the quick brown fox jumped over the lazy dogs')"
tripleee

@triplee: Я поняття навіть не маю, що хоче зробити ОП. Єдина реальна перевага побудови рядка, що цитується, для передачі - evalце те, що вам ще не потрібно його передавати eval; Ви можете зберегти його в параметрі та використовувати його пізніше, можливо, кілька разів з різними командами. Однак ОП не вказує на те, що це випадок використання (і якби воно було, то, можливо, було б краще поставити назви файлів у масив, хоча це теж хитро).
rici

0
find ./  | grep " "

отримає вам файли та каталоги містить пробіли

find ./ -type f  | grep " " 

отримає вам, що файли містять пробіли

find ./ -type d | grep " "

отримає вам, що каталоги містять пробіли


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