Розширення з * .txt в оболонці не працює, якщо не існує .txt-файлу


10

Я розігрувався з розширенням, і помітив своєрідну поведінку. Я намагався робити:

echo ./*.txt

І у мене в поточному каталозі не було жодного .txt-файлу. Вихід, який я отримав:

./*.txt

Мені просто цікаво: Чому я це отримав? Я очікував, що не отримаю жодного результату.

PS: Коли у мене був .txtфайл, розширення було правильно інтерпретовано. Іншими словами, скажіть, у мене був файл, smthn.txtвідлуння насправді лунало current_directory/smthn.txt.

Відповіді:


15

Адаптація зі сторінки "bash shell man",

bash сканує кожне слово для символів *,? та [. Якщо з’являється один з цих символів, то слово вважається візерунком і замінюється алфавітно відсортованим списком імен файлів, що відповідають шаблону. Якщо не знайдено відповідних імен файлів, а параметр оболонки nullglob не включений, слово залишається незмінним. Якщо параметр nullglob встановлений, а збіги не знайдено, слово видаляється.

У цьому випадку я припускаю, що nullglob не ввімкнено, тому слово залишається незмінним - значить, результат ви бачите.


2
Ви можете змінити поведінку так: shopt -s nullglobвийдуть порожні рядки для невідповідних шаблонів і shopt -u nullglob(стандартне налаштування) дасть сам шаблон.
PerlDuck

13

Я очікував, що не отримаю жодного результату.

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

Припустимо, ви увімкнули nullglob( shopt -s nullglob) і перебуваєте в каталозі, де жоден файл не відповідає *.txt. Тоді *.txtдійсно розшириться ні до чого - не до порожнього поля, але до жодного поля - як ви очікували. Але це матиме ці результати:

  • ls *.txtперерахував би всі файли в поточному каталозі (крім прихованих файлів), тому що це lsробиться, коли ви не передаєте йому жодних аргументів імені файлу.
  • cat *.txtчитав би зі стандартного вводу , тому що коли catнемає аргументів назви файлів, це як би ти побіг cat -. Якщо працює інтерактивно, він сидить навколо очікування введення. Багато команд ведуть себе таким чином.
  • cp *.txt dest/не вдалося б із помилкою cp: missing destination file operand after 'dest/'. Це не катастрофа, але це заплутано і зовсім відрізняється від тихого успіху, який, мабуть, бажаний.
  • file *.txt, і різні інші програми, що не мають особливої ​​поведінки для випадку нульових аргументів імені файлів, все одно не зможуть отримати повідомлення про помилку або використання, коли жодне не передано.
  • Навіть випадки, які інтуїтивно відчувають, що вони повинні працювати, часто не мали б. printf 'Got file: "%s"\n' *.txtдрукував би Got file: ""замість нічого.
  • Випадкове відмова процитувати входження *, ?і [що НЕ передбачається розширити оболонку б частіше проводити очевидно неправильні результати, але таким чином , що може бути важко зрозуміти. Наприклад, якщо в поточному каталозі не починалися імена файлів gedit, тоді apt list gedit*(де apt list 'gedit*'було призначено), вони стануть справедливими apt listта перелічують усі доступні пакети.

Тож добре, що ви не отримуєте такої поведінки, не вимагаючи цього. Ймовірно, найбільш поширена практична ситуація , яка насправді спрощено nullglobє for f in *.txt. Дивіться також це питання (з яким пов'язана відповідь Сергія Колодяжного ).

Важче відповісти на те, чому failglob- там, де помилка розширення має глобус, який не відповідає жодному файлу, - це не за замовчуванням у bash. Я вважаю , що відповідь Сергія Колодяжного фіксує причину цього навіть без прямого звернення до нього. Збереження нерозширюваних глобусів, не створюючи помилок розширення, є (можливо, на жаль) стандартизованою поведінкою, а також традиційною та, таким чином, очікуваною, поведінкою. Хоча bash не намагається бути повністю сумісним з POSIX, якщо не викликається ім'ям shабо передається --posixопція, багато варіантів дизайну, навіть якщо вони не перебувають у режимі POSIX, слідують POSIX безпосередньо. Їм довелося вибрати деяку поведінку, і є недоліки, пов’язані з тим, щоб іти проти очікувань користувачів.


Я думаю, що це найменш історично впливовий аспект справи, тому я його остаточно врятував ... Але варто згадати, що в nullglobповедінці є щось дещо концептуально дивне .

nullglobспочатку здається елегантним, оскільки, синтаксично , він розглядає випадки нульових файлів відповідності не інакше, ніж регістр одного, двох чи будь-якого іншого числа. Команди, які ми виконуємо, для яких глобуси перетворюються на аргументи, не мають тенденцію трактувати їх так само, як описано вище. Але синтаксично це принаймні відчуває себе правильно, що, на мою думку, є мотивацією для вашого питання.

І все ж, є ще одна, більш тонка непослідовність, nullglobяка не стосується - вона насправді посилюється. Випадок нульових глобусних символів ("макіяжних символів") трактується дуже різним чином, ніж один, два, або будь-яке інше число. Наприклад, shopt -s nullglobякщо ab?d?fфайл не відповідає жодному файлу, він видаляється; якщо ab?dне відповідає жодному файлу, він видаляється; але якщо abне відповідає жодному файлу (тобто, якщо немає файлу, ім'я якого точно ab), він все одно не буде видалений. Звичайно, це було б катастрофою, якби його було видалено, тому що воно взагалі може не мати на меті посилання на існуючий файл у поточному каталозі; він може навіть не посилатися на файл. Але це все ж усуває будь-яку надію на повну послідовність.

Три баші поведінки передбачають - за замовчуванням обробляти глобуси, які не відповідають жодному файлу, як ніби вони не були глобусами і передавали їх нерозкритим, поведінку, яку ви очікували від їх обробки (якщо ви помилуєте цей дивний поворот фрази) як означають всі нулі файлів, які відповідають ( nullglob), і безпечну поведінку при розгляді їх помилок ( failglob) - всі представляють різні підходи до неоднозначності, притаманної оболонці, не в змозі знати, чи призначене якесь конкретне слово ім'я файлу. Оболонка виконує свої розширення, не знаючи, як конкретні команди, з якими ви викликаєте, будемо ставитися до їхніх аргументів.

Це один із багатьох випадків розділення проблем . У системах, дизайн яких відповідає філософії Unix, кожна частина має на меті зробити одне і зробити це добре . Оболонка обробляє текст у команди та аргументи та викликає ті команди, більшість з яких є зовнішніми для самої оболонки. Це, як правило, набагато приємніше і універсальніше, ніж системи, де зовнішні команди самі відповідають за виконання цих перетворень (як це стосується традиційних процесорів команд в DOS і Windows). Але це має свої випадкові мінуси.


Мабуть, bash-4.3.39 (2) не має failglob. Таким чином, він не може бути типовим, оскільки він не завжди підтримувався.
Руслан

6

Основна причина полягає в тому, що це стандартна поведінка, визначена POSIX - стандартом, який охоплює мову командної оболонки і, серед іншого, відповідність шаблону (оболонки, такі як bash, dashshell - за замовчуванням Ubuntu /bin/sh, і kshдотримуйтесь цього стандарту). З розділу 2.13.3 Шаблони, що використовуються для розширення імені файлів :

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

Це, звичайно, має побічний ефект - відповідність назви файлу, яка може бути буквально *.txt. nullglobВаріант в bashі zshможуть допомогти: якщо цієї опція включена з допомогою shopt -s nullglob(і вона не включена за замовчуванням , яка відноситься до цього питання), то globstar буде розширено порожній рядок , якщо не знайдені імен файлів. ksh93має власний вдосконалений механізм узгодження візерунків, який досягає того ж ефекту~(N)*.txt

Дивіться також Чому nullglob не використовується за замовчуванням?

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