Як передати підстановку '*' в параметр шляху команди пошуку через змінну в скрипті?


9

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

З командного рядка це легко. Наведені нижче приклади працюють.

find  te*/my\ files/more   -print
find  te*/'my files'/more  -print
find  te*/my' 'files/more  -print

Вони знайдуть файли, наприклад, terminal/my files/moreта tepid/my files/more.

Однак мені потрібно, щоб це було частиною сценарію; що мені потрібно, це щось подібне:

SEARCH='te*/my\ files/more'
find ${SEARCH} -print

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

find: te*/my\\’: No such file or directory
find: files/more’: No such file or directory

Спроба використовувати лапки також не вдається.

SEARCH="te*/'my files'/more"
find ${SEARCH} -print

Це повертає наступні помилки, ігноруючи значення цитат:

find: te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory

Ось ще один приклад.

SEARCH='te*/my files/more'
find ${SEARCH} -print

Як і очікувалося:

find: te*/my’: No such file or directory
find: files/more’: No such file or directory

Кожна варіація, яку я спробував, повертає помилку.

У мене є рішення, яке потенційно небезпечно, оскільки воно повертає занадто багато папок. Я перетворюю всі пробіли в знак запитання (однозначний символ символів) таким чином:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print

Це еквівалент:

find te*/my?files/more -print

Це повертає не тільки правильні папки, але й те terse/myxfiles/more, чого не слід.

Як я можу досягти того, що намагаюся зробити? Google мені не допоміг :(


@KasiyA я використовую bash; ви повинні використовувати щось інше, як я раніше не бачив цієї конструкції. Команда призводить SEARCH: command not foundдо виконання команди find -print.
Падді Ландау

Постріл у темряві, а як бути з цитуванням? find "${SEARCH}" -print?
Алаа Алі

@AlaaAli Ні, це не працює, тому що котирування заважає Bash використовувати підстановку. Він шукатиме шлях конкретно з назвою (на моєму прикладі) te*/'my files'/more.
Падді Ландау

Відповіді:


9

Точно така ж команда повинна добре працювати в сценарії:

#!/usr/bin/env bash
find  te*/my\ files/ -print

Якщо вам потрібно мати його як змінну, він стає трохи складнішим:

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print

УВАГА:

Використання evalподібного не є безпечним і може призвести до виконання довільного та, можливо, шкідливого коду, якщо імена ваших файлів можуть містити певні символи. Див Баш Довідку 48 для деталей.

Краще пройти шлях як аргумент:

#!/usr/bin/env bash
find "$@" -name "file*"

Інший підхід полягає у тому, щоб уникнути findвзагалі та використовувати розширені функції башма та глобуси:

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done

Параметр globstarbash дозволяє використовувати **для рекурсивного зіставлення:

globstar
      If set, the pattern ** used in a pathname expansion con
      text will match all files and zero or  more  directories
      and  subdirectories.  If the pattern is followed by a /,
      only directories and subdirectories match.

Щоб змусити його діяти на 100%, як знайти і включити точкові файли (приховані файли), використовуйте

#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done

Ви можете навіть echoїх безпосередньо без циклу:

echo te*/my\ files/**

2
Я поставив це як відповідь через корисні коментарі, які зробив тердон (не забуваючи корисних коментарів інших). Я використовував здатність Баша в глобальному командному рядку для передачі декількох шляхів до мого сценарію, замість того, щоб сценарій намагався розібратися. Це добре працює.
Падді Ландау

2

Як щодо масивів?

$ tree Desktop/ Documents/
Desktop/
└── my folder
    └── more
        └── file
Documents/
└── my folder
    ├── folder
    └── more

5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}" 
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder

(*)розширюється в масив того, що відповідає підстановці. І "${SEARCH[@]}"розширюється на всі елементи масиву ( [@]), при цьому кожен окремо цитується.

З запізненням я усвідомлюю, що виявити себе має бути здатним до цього. Щось на зразок:

find . -path 'D*/my folder/more/'

Розумна ідея, але, на жаль, вона не працює. Чому? Тому що сам шлях утримується в змінній; отже, INPUTPATH='te*/my files/moreі SEARCH=(${INPUTPATH}). Незалежно від того, наскільки я варіюю те, як я це роблю, все одно я отримую нефункціональний результат. Це здається неможливим!
Падді Ландау

Це все вірно, звичайно, але ОП потрібно робити це за сценарієм. Це змінює речі, оскільки розширення макетів стає значно складнішим, і це не працює.
тердон

@PaddyLandau В такому випадку, чому б вам не використовувати find«s -pathфільтр? Він використовує підстановку і, безумовно, не потребує розширення.
муру

@muru Це цікаво; Я про це не знав -path. За останні 10 хвилин я зрозумів відповідь: використовуй eval! Здається, це простіше, ніж -path.
Падді Ландау

2
@terdon та muru та всі: Дякую. Я почув те, що ви всі сказали, і зрозумів, що я повинен змусити свій сценарій зробити одне, і дозволити Bash globbing пропускати декілька файлів або шляхів до сценарію. Я змінив свій сценарій таким чином. Він добре працює і краще відповідає філософії Linux. Ще раз дякую вам!
Падді Ландау

0

Я нарешті з’ясував відповідь.

Додайте зворотний нахил у всі пробіли:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }

На даний момент, SEARCHмістить te*/my\ files/more.

Потім використовуйте eval.

eval find ${SEARCH} -print

Це так просто! Використання evalобходить інтерпретацію, яка ${SEARCH}є змінною.



@terdon Дякую за попередження Назад до дошки для малювання!
Падді Ландау

Так, це напрочуд хитро. Я просто оновив свою відповідь іншим підходом: чому б не використовувати натомість глобінг? Якщо це все ще не працює, я пропоную вам поставити нове запитання на Unix & Linux, в якому пояснюється, яка ваша кінцева мета і чому потрібно мати шаблон як змінну. Цей тип речей швидше, щоб отримати кращу відповідь там.
тердон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.