Как объединить содержимое файлов в PowerShell. И при этом не пострадать

в 14:39, , рубрики: bug, powershell

Что надо было сделать

Однажды мне понадобилось объединить пачку текстовых файлов, лежащих в одной директории, в один файл. Делать руками этого не хотелось и мне на помощь, как всегда, пришёл Гугл! Я много слышал о мощности такого средства, как PowerShell, и решил использовать его для этой "мегазадачи". Хотя бы потому, что с убогостью средства cmd.exe я знаком не по наслышке. Ну а делать это руками — не наш путь.

Что пошло не так

Гугл подсказал мне, что сделать это можно простой командой

Get-ChildItem -Filter *.log | Get-Content | Out-File result.txt

"Действительно круто! Просто Unix-way какой-то!" — подумал я. Скопировал команду, слегка модифицировал её и нажал Enter. Курсор перешёл на новую строку… и больше ничего. В файловом менеджере я открыл итоговый файл — в нём действительно было что-то похожее на нужный результат. В нём было много строк из исходных файлов. Вернувшись в консоль я увидел, что процесс всё ещё… в процессе. Помог Ctrl+C.

Присмотревшись к размеру файла я увидел, что он как-то подозрительно велик. ЕГо размер превышел 100 Мегабайт. Хотя водные данные не были такими большими.

Почему это случилось?

Всё дело в моей "лёгкой модификации". Мне просто не нужен был фильтр по расширению. Да и параметр этот не является обязательным. И получилось, что команда создала результирующий файл, увидела, что он есть в директории, прочитала его и снова записала своё содержимое в конец и делала это, пока я не нажал Ctrl+C Никак по другому непрерывный рост вызодного файла я объянить для себя не смог

Я повторил это в "стерильных" условиях. Для простоты и чистоты эксперимента делаю в отдельной директории, так как боюсь убить рабочую машину

  1. Создаю текстовый файл

    echo "Hello world" > hello.txt

  2. Выполняю команду

    Get-ChildItem | Get-Content | Out-File result.txt

    или в короткой форме

    dir | cat | Out-File result.txt

    Проблема повторяется. Результирующий файл растёт, пополняясь строкой из исходного (или строками из самого себя?). За 10 секунд выполнения:

    • одна строка исходного файла превращается в 400 тысяч строк
    • размер файла вырос с 11 байт до почти 8 мегабайт
    • процессор грузится примерно на 20-25 %.
    • перегрузок дисковой подсистемы или оперативной памяти при этом нет. Видимо, PowerShell хорошо оптимизирован в части работы с этими компонентами. )

Так же интересно — если в качестве параметра последней команде указать имя единственного файла в директории, то, конечно же, как вы уже догадались барабанная дробь… в файл запишется пустота!

Вот такая вот "интересная" логика работы

Что получилось

Созданный на первом шаге файл начинает расти. Это поведение как минимум непредсказуемое.
Так же удивило, что операционная система продолжает нормально работать. Файл медленно (или не очень?) растёт, не блокируя работу пользователя.

Чем опасно

Незаметное заполнение дискового пространства.

Как избежать

Фильтровать список входных файлов:

Get-ChildItem -Filter *.log | Get-Content | Out-File result.txt

Но и это не спасёт, если и входные и выходной файл у вас подходят под условие фильтра

P.S.

Я использую версию PowerShell 5.1.17134.407. Кстати, в попытках узнать я испробовл все известные мне способы/логику и здравый смысл (а именно флаги типа **-Version --version -v -h***). Но это не помогло. Выручил, как всегда, Stackoverflow. Вот как можно узнать версию PowerShell

$PSVersionTable.PSVersion

Этот ответ собрал почти 3000 "лайков"! Это конечно меньше, чем ответ на вопрос как закрыть vim, но тоже считаю показательно!

А вообще PowerShell действительно мощная штука (хотя бы в сравнении с cmd.exe) и я, конечно, буду продолжать ею пользоваться!

Автор: solo12zw74

Источник

* - обязательные к заполнению поля