Как пройти строку с трейлинговым пробелом до подпроцесса Python

Вы можете сделать файл настолько большим или маленьким, насколько захотите, особенно на Linux tmpfs.

df -h /tmp

Filesystem      Size  Used Avail Use% Mounted on
tmpfs            12G  472K   12G   1% /tmp

Мы можем просто сделать разреженный файл.

for cmd in  \
       'dd bs=1024k seek=20k of=' \
       'ls -slh '
do      eval "$cmd/tmp/file"
        echo
done    </dev/null

0+0 records in
0+0 records out
0 bytes (0 B) copied, 0.000152051 s, 0.0 kB/s

0 -rw-r--r-- 1 mikeserv mikeserv 20G Dec 24 20:19 /tmp/file

Видите? Он использует 0 блоков дискового пространства, но его видимый размер составляет 20 гигабайт.

Затем вы можете просто fdisk / tmp / file . Я только что создал на нем таблицу разделов. Вот fdisk -l :


Disk /tmp/file: 20 GiB, 21474836480 bytes, 41943040 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x057d787a

Device     Boot    Start      End  Sectors Size Id Type
/tmp/file1          2048 20973567 20971520  10G 83 Linux
/tmp/file2      20973568 31459327 10485760   5G  5 Extended
/tmp/file3      31459328 41943039 10483712   5G 83 Linux

После того, как таблица была записана, она действительно использует маленький бит пространства:

ls -lsh /tmp/file

8.0K -rw-r--r-- 1 mikeserv mikeserv 20G Dec 24 20:21 /tmp/file

Однако вы не знаете.

df -h /tmp

Filesystem      Size  Used Avail Use% Mounted on
tmpfs            12G  480K   12G   1% /tmp

И вы можете разреженно расширить файл таким же образом:

for cmd in  \
       'dd bs=1024k seek=30k of=' \
       'ls -slh '  'fdisk -l '
do      eval "$cmd/tmp/file"
        echo
done    </dev/null

0+0 records in
0+0 records out
0 bytes (0 B) copied, 9.8239e-05 s, 0.0 kB/s

8.0K -rw-r--r-- 1 mikeserv mikeserv 30G Dec 26 14:24 /tmp/file

Disk /tmp/file: 30 GiB, 32212254720 bytes, 62914560 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x057d787a

Device     Boot    Start      End  Sectors Size Id Type
/tmp/file1          2048 20973567 20971520  10G 83 Linux
/tmp/file2      20973568 31459327 10485760   5G  5 Extended
/tmp/file3      31459328 41943039 10483712   5G 83 Linux
1
22.09.2018, 19:26
1 ответ

Здесь, как указал @jordanm, ваша проблема заключается в том, что вы вызываете mvпо имени файлов для каждого из каталогов, пройденных os.walk, но os.walkне изменяет текущий рабочий каталог.

Таким образом, для тех файлов, которые находятся в подкаталогах, это не сработает.

Вам нужно передать полный путь к файлу в mv, что-то вроде os.path.join(dirpath, name).

В идеале вы хотели бы, чтобы ходьба меняла каталоги по мере продвижения, как это делаютperlFile::Findв finddepth()или BSD/GNU find -execdir, что сделает его более безопасным и позволит избежать проблем с слишком глубокие деревья каталогов, но я не думаю, что вы можете легко сделать это с помощью python's os.walk().

Теперь есть несколько других проблем с вашим кодом:

Уязвимость внедрения команд

Теперь конечные пробелы — это наименьшая из ваших забот в этом:

subprocess.call("mv '%s' '%s'"%(name,name.strip()),shell=True)

Это в основном уязвимость внедрения команд (, например, файл с именем'$(reboot)'(с кавычками )).

Как правило, не вставляйте произвольный текст в строки, интерпретируемые как шелл-код (или код на любом языке, который может причинить какой-либо вред ).

С вашим кодом (вариант, использующий форму 'mv "%s" "%s"'), может привести к той же ошибке, например, с файлами с именами randconf $xили randconf $(test).

Здесь используйте:

subprocess.call(("mv", "--", name, name.strip()),shell=False)

Если вам нужно использовать оболочку, лучшим способом передачи данных в эту оболочку является использование переменных среды:

os.putenv("OLD", name)
os.putenv("NEW", name.strip())
subprocess.call('mv -- "$OLD" "$NEW"',shell=True)

Запуск оболочки также обходится дорого. Особенно в системах, где shна самом деле представляет собой большую полнофункциональную -оболочку, такую ​​как bash, ksh93или zsh, загрузка и инициализация которой обычно занимает значительное время.

Пока вы вызываете оболочку, вы можете выполнять поиск и переименование в коде оболочки.

Неоднозначностьmv

mvне является командой с лучшим интерфейсом(cpи lnимеют те же проблемы ). Проблема в том, что mvделает много разных вещей, но не в зависимости от того, что/как вы об этом спрашиваете, а в зависимости от контекста.

mv A B

Либо

  • переименовывает A в B/A, если B существует и имеет тип каталог или символическую ссылку на каталог в той же файловой системе
  • делает то же самое, но с копией (с сохранением как можно большего количества атрибутов )с последующим удалением, если переименование выходит за границы файловой системы
  • выполняет переименование, иначе (удаляет цель, если она существовала ранее)

Здесь вам просто нужен базовый rename()системный вызов или, может быть, лучше rename(), который не затирает уже существующий файл (, как в Linux renameat2(... RENAME_NOREPLACE)), что также облегчит проблемы, такие как "randconf_1 "и "randconf_1 "переименованы в randonf_1.

В GNU mvвы можете сделать это с помощью:

mv -nT -- "$old" "$new"

но это не переносимо. (Также обратите внимание, что GNU mvне использует renameat2()в Linux, поэтому имеет (второстепенное здесь )состояние гонки)

В любом случае, в pythonвам не нужно вызывать отдельную программу только для переименования файла.

os.rename(name, name.strip());

(Не думаю, что pythonимеет привязку к Linux'renameat2())

пробел против пробела

python's strip()удаляет начальные и конечные пробельные символы. Здесь все строки начинаются с randconf, так что это то же самое, что и rstrip(). пробел включает символ пробела ASCII, но всевозможные другие вертикальные и горизонтальные символы пробела (только символы ASCII, хотя ), такие как TAB, LF, CR...

Поскольку вы ищете файлы, которые содержат пробелов в любом месте строки,вы можете не переименовывать некоторые имена файлов, которые заканчиваются пробелом (, например "randconf_\t", который не содержит пробела ), или вызывать mvдля имен файлов, которые не заканчиваются пробелом (, например"randconf_x y").

Вы можете использовать fnmatch.filter(filenames, "randconf* ")и rstrip(" "), если вас интересуют только конечные пробелы

Эквивалент оболочки POSIX:

Сделайте это с помощью оболочки POSIX и синтаксиса утилиты:

find. -depth -name 'randconf*[[:space:]]' ! -type d -exec sh -c '
  for file do
    newfile=${file%"${file##*[![:space:]]}"}
    [ -e "$newfile" ] || [ -L "$newfile" ] || mv -- "$file" "$newfile"
  done' sh {} +

Или немного надежнее с утилитами GNU

find. -depth -name 'randconf*[[:space:]]' ! -type d -execdir bash -O extglob -c '
  for file do
    newfile=${file%*([[:space:]])}
    mv -nT -- "$file" "$newfile"
  done' sh {} +

(обратите внимание, что он удаляет все символы, считающиеся пробелами в текущей локали, а не только символы ASCII, как в python; измените локаль на C, чтобы соответствовать только пробелу ASCII ).

2
27.01.2020, 23:31

Теги

Похожие вопросы