Рекурсивные глобусы. ** или */** globstar различается в zsh, Bash, Python и Ruby.Python

Программы на Python
Ответить
Anonymous
 Рекурсивные глобусы. ** или */** globstar различается в zsh, Bash, Python и Ruby.

Сообщение Anonymous »

Предположим, у вас есть такое дерево каталогов:

Код: Выделить всё

$ tree /tmp/test
/tmp/test
├── dir_a
│   ├── dir a\012file with CR
│   ├── dir a file with spaces
│   └── sub a directory
│       └── with a file in it
├── dir_b
│   ├── dir b\012file with CR
│   └── dir b file with spaces
├── dir_c
│   ├── \012
│   ├── dir c\012file with CR and *
│   └── dir c file with space and *
├── file_1
├── file_2
└── file_3

4 directories, 11 files
(ЗДЕСЬ приведен сценарий для этого. \012 — это \n, чтобы сделать сценарий более сложным. Существует .hidden тоже там.)
Похоже, существуют существенные различия в реализации рекурсивного подстановки между Bash 5.1, zsh 5.8, Python pathlib 5.10, модулем Python glob с включенной рекурсией и Ruby 3.0.< /p>
Это также предполагает, что shopt -s globstar с Bash и cwd является текущим рабочим каталогом и для этого примера установлено значение /tmp/test.
p>
Вот что делает Bash:
  • Только файлы и каталоги в cwd. т.е. 3 каталога, 3 файла
  • Все файлы и каталоги в дереве, корнем которых является cwd, но не cwd — файлы 4 и 11
  • Только каталоги в дереве, корнем которых является cwd, но не включая cwd – 4 и 0
  • Все каталоги в cwd и все файлы, КРОМЕ файлов в cwd — 4 и 8 файлов, поскольку рекурсия запускается только в подкаталогах
  • То же, что ** – 4 и 11
  • Только каталоги в дереве — 4 и 0 файлов
  • Только каталоги ниже второго уровня и файлы ниже первого — 1 и 8.
Если я запущу этот скрипт в Bash 5.1 и zsh 5.8, они результаты разные:

Код: Выделить всё

# no shebang - execute with appropriate shell
# BTW - this is how you count the result since ls -1 ** | wc -l is incorrect
# if the file name has \n in it.
cd /tmp/test || exit
[ -n "$BASH_VERSION" ] && shopt -s globstar
[ -n "$ZSH_VERSION" ] && setopt GLOBSTARSHORT # see table
dc=0; fc=0
for f in **; do              # the glob there is the only thing being changed
[ -d "$f" ] && (( dc++ ))
[ -f "$f" ] && (( fc++ ))
printf "%d, %d \"%s\"\n" $dc $fc "$f"
done
printf "%d directories, %d files" $dc $fc
Результаты (выраженные в виде X, Y для каталогов X и файлов Y для этого примера каталога с использованием указанного glob. Проверив или запустив эти сценарии, вы можете увидеть, что посещает glob. ):




glob
Bash
zsh
zsh GLOBSTARSHORTpathlib
python glob
ruby



3,3
3,3
3,3
3,3
3,3
3,3

4,11
3,3
4,11
5,0‡
5,11‡
3,3

4,0
4,0
4,0
5,0‡
5,0‡
5,0‡
< /tr>
4,8
1,7
1,8
4,0
4,8
1,7

4,11
4,11
4,11
4,12†
4,11
4,11

4,0
4,0
4,0
4,12†
4,0
4,0

1,8
1,8
1,8
1,9†
1,8
1,8




‡ Число каталогов, равное 5, означает, что также возвращается CWD.
† Pathlib Python сохраняет скрытые файлы; остальные нет.
Сценарий Python:

Код: Выделить всё

from pathlib import Path
import glob

tg="**/*"  # change this glob for testing

fc=dc=0
for fn in Path("/tmp/test").glob(tg):
print(fn)
if fn.is_file():
fc=fc+1
elif fn.is_dir():
dc=dc+1

print(f"pathlib {dc} directories, {fc} files\n\n")

fc=dc=0
for sfn in glob.glob(f"/tmp/test/{tg}", recursive=True):
print(sfn)
if Path(sfn).is_file():
fc=fc+1
elif Path(sfn).is_dir():
dc=dc+1

print(f"glob.glob {dc} directories, {fc} files")
Сценарий Ruby:

Код: Выделить всё

dc=fc=0
Dir.glob("/tmp/test/**/").
each{ |f| p f; File.directory?(f) ? dc=dc+1 : (fc=fc+1 if File.file?(f)) }

puts "#{dc} directories, #{fc} files"
Поэтому единственными глобусами, с которыми все согласны (кроме скрытого файла), являются *, **/* и */**/*
Документация:
  • Bash:
    < em>два соседних символа '*, используемые как один шаблон, будут соответствовать всем файлам и нолю или более каталогам и подкаталогам.
  • zsh: a) setopt GLOBSTARSHORT устанавливает **.c эквивалентным **/*.c и b) '**/' эквивалентен '(*/)#'; обратите внимание, что это соответствует файлам как в текущем каталоге, так и в подкаталогах.
  • pathlib: ** что означает «этот каталог и все подкаталоги рекурсивно»
  • python glob: если рекурсивно установлено значение true, шаблон ** будет соответствовать любым файлам и нулю или более каталогов, подкаталогов и символических ссылок на каталоги. Если за шаблоном следует os.sep или os.altsep, файлы не будут совпадать.
  • ruby: ** Сопоставляет каталоги рекурсивно, если за ними следует /. Если этот сегмент пути содержит какие-либо другие символы, он аналогичен обычному *.
Вопросы:
  • Правильны ли мои предположения о том, что должен делать каждый шарик?
  • Почему Bash — единственный рекурсивный инструмент с **? (если вы добавите setopt GLOBSTARSHORT в zsh, результат будет аналогичен **
  • Is разумно сказать себе, что **/* работает для всех


Подробнее здесь: https://stackoverflow.com/questions/706 ... n-and-ruby
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»