Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Скриптовые языки администрирования Windows (http://forum.oszone.net/forumdisplay.php?f=102)
-   -   [решено] Пакетное преобразование xls ->xlsx (xls -> csv) (http://forum.oszone.net/showthread.php?t=349157)

Ent0ma 07-07-2021 08:19 2961600

Пакетное преобразование xls ->xlsx (xls -> csv)
 
Требуется скрипт для преобразования всех файлов в определенной папке xls в xlsx (или csv).

greg zakharov 07-07-2021 09:41 2961605

Через COM Excel.Application: открыли документ, перевели в нужный формат, сохранили результат. А ежели в системе не развёрнут офисный пакет, на коленке пишется парсер ФС xls файла, конвертирующий данные таблиц в xml с последующей упаковкой в zip и сменой расширения на xlsx (в случае с csv можно использовать методы БД). И третий вариант - онлайн конверторы. curl'ом слать файлы на конвертацию, и если возвращается статус 200 (или любой иной удачный, согласно API), забрать результат также curl'ом.

Ent0ma 07-07-2021 10:22 2961606

Цитата:

Цитата greg zakharov
Через COM Excel.Application: открыли документ, перевели в нужный формат, сохранили результат. »

Офисный пакет установлен, проблема в том что там на месте пользователи не очень то дружат с компом/экселем. А файлов в папке много, которые требуется пересохранить - выгрузка из 1С сохраняет всегда в xls. Идеальный вариант: пользователь запускает скрипт или он отрабатывает автоматически конвертируя все файлы xls в папке в xlsx. VBA не хочу, сколько будет стоить скрипт на posh?

YuS_2 07-07-2021 12:57 2961612

Цитата:

Цитата Ent0ma
сколько будет стоить скрипт на posh? »

он будет бесценным (ну или пару кружек пива) :)
Код:

param (
        $folderin = '.\TEST',
        $folderout = '.\OUT',
        $flt = '*.xls',
        $ext = '.xlsx'
)

$fldin = gi $folderin
if (!(test-path $folderout)){md $folderout|out-null}
$fldout = gi $folderout
$excel = new-object -com "Excel.Application"
dir $fldin.fullname -filter $flt -file|%{
        $workbook = $excel.workbooks.open($_.fullname)
        $newname = $fldout.fullname + '\' + $_.basename + $ext
        $workbook.saveas($newname, 51)
}
$excel.quit()


greg zakharov 07-07-2021 13:54 2961616

YuS_2, дружище, ты забыл высвободить COM, только его экземпляр закрыл.

Elven 07-07-2021 13:55 2961617

YuS_2, есть же beerware license

Вообще есть вот такой вариант
Сейчас нет возможности проверить, но скорей всего все это сделать можно будет в пару команд.

YuS_2 07-07-2021 14:21 2961619

Цитата:

Цитата greg zakharov
забыл высвободить COM, только его экземпляр закрыл. »

Да чего ему сделается? сессию закроют, объект попустит :)
Ну или добавить в конец скрипта, если принципиально:
Код:

[runtime.interopservices.marshal]::releasecomobject($excel)
Цитата:

Цитата Elven
есть же beerware license »

то шутка была... мне в реале хватает на пиво, чтобы за такие скрипты на пиво сшибать. Просто виртуального - можно похлебать... :)

Elven 07-07-2021 14:56 2961623

YuS_2, beerware license не обязует пиво покупать, только намекает что автор кода не против этого.

megaloman 07-07-2021 15:56 2961626

Цитата:

Цитата Ent0ma
VBA не хочу »

А vbs?
Код:

BoxIn = "Z:\Box_In"
BoxInOld = "_Old.xls"

Set Book = CreateObject("Excel.Application")
Book.Visible = False 'True  ' False

With CreateObject("Scripting.FileSystemObject")
    If Not .FolderExists(BoxIn + "\" + BoxInOld) Then Call .CreateFolder(BoxIn + "\" + BoxInOld)
   
    For Each iFile In .GetFolder(BoxIn).Files
        If LCase(Right(iFile, 4)) = ".xls" And Not .FileExists(iFile + "x") Then
            With Book
                .Workbooks.Open iFile
                Call .ActiveWorkbook.SaveAs(iFile + "x", 51, , , , False)
                .ActiveWorkbook.Close
            End With
        End If
        .MoveFile iFile, BoxIn + "\" + BoxInOld + "\" + iFile.Name
    Next
End With
Book.Quit


alpap 07-07-2021 17:02 2961631

Цитата:

Цитата Elven
Вообще есть »

не хочет он с xls работать:
---
Exception: C:\...\PowerShell\Modules\ImportExcel\7.1.2\Public\Import-Excel.ps1:99
Line |
99 | … throw "Import-Excel does not support reading this extensi …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Import-Excel does not support reading this extension type .xls
---

DJ Mogarych 07-07-2021 18:24 2961635

В составе Офиса есть конвертер.
Код:

excelcnv.exe -oice file.xls file.xlsx

Iska 07-07-2021 19:43 2961640

YuS_2, здесь:
Скрытый текст

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

Цитата:

Цитата DJ Mogarych
В составе Офиса »

DJ Mogarych, он точно в составе Office, а не отдельного Microsoft Office Compatibility Pack for Word, Excel, and PowerPoint File Formats?
Вопрос снимается. Для старого Office — это точно отдельный конвертер, а в составе новых, надо полагать, идёт в комплекте.


Оставлю сие здесь себе для памяти:

excelcnv command line: Hexacorn | Blog Excellent Conversions (and downloads)

И всё ещё не нашёл ответа про параметр «-nme».

YuS_2 07-07-2021 20:34 2961650

Цитата:

Цитата Iska
надо быть аккуратнее и не забывать про короткие имена файлов. »

надо отключать пережитки прошлого... напрочь... в том числе и ISE... тогда напрягать ресурсы памяти не надо будет. :)

Код:

fsutil 8dot3name set 1

greg zakharov 07-07-2021 22:30 2961654

Цитата:

Цитата YuS_2
Да чего ему сделается? сессию закроют, объект попустит

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

alpap 07-07-2021 23:03 2961657

Цитата:

Цитата greg zakharov
никто не знает, когда сессия завершится »

я, если и закрываю сессию, то у меня все равно EXCEL висит в процессах, $Excel.Quit() не помогает, приходится еще такую строчку писать:
Stop-Process -Name EXCEL
а вот WINWORD не висит, закрывается по $Word.Quit(), хотя для надежности все равно пишу и Stop-Process -Name WINWORD

greg zakharov 07-07-2021 23:18 2961658

alpap, ReleaseComObject по идее должно быть достаточно.

alpap 07-07-2021 23:53 2961662

нет, попробовал
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
недостаточно, вернулся на spps -n Excel

Iska 08-07-2021 00:13 2961666

Цитата:

Цитата YuS_2
Код:

fsutil 8dot3name set 1
»

У себя-то отключить можно… А если — не у себя?! Так что, пока — помним ;).

greg zakharov 08-07-2021 00:15 2961668

alpap, речь не о завершении связанного процесса, а об уменьшении количества ссылок на указанный COM. Сигнал к завершению процессу подаётся, если количество ссылок равно нулю (значение возвращаемое ReleaseComObject). А вот если по истечении кванта времени, отведённого на завершение, процесс продолжает "болтаться", ничего не остаётся, как прибивать его вручную.

YuS_2 08-07-2021 08:17 2961682

Цитата:

Цитата greg zakharov
ReleaseComObject по идее должно быть достаточно. »

Тоже так думал... но как показала практика, микрософт (как и я в данном случае :) ), плевать хотел на висяки в памяти и освобождение ресурсов, предназначенным для этого способом, работает как того захочется "великому электронобайту" от микрософт... может сработать, а может и не сработать. :)
Придется таки следить за наличием процесса в памяти (желательно по PID, чтобы не убить лишнего) и убивать его по завершении скрипта... или попробовать найти более вменяемый способ (мне лень этим заниматься, пока)...

Цитата:

Цитата greg zakharov
А вот если по истечении кванта времени, отведённого на завершение, процесс продолжает "болтаться", ничего не остаётся, как прибивать его вручную. »

Вот-вот. Но задержки ставил, так что пока корректного способа не обнаружил...

antivan 12-07-2025 17:51 3038853

Код:

[String]$PathToAnalise = "D:\Обменка\Счета\"

$excel = New-Object -ComObject Excel.Application # открываем экземпляр Excel, он будет один на весь скрипт
For(  ;  ;  ) {
    Get-ChildItem -Path $PathToAnalise *.xls -File -Recurse -Attributes `
        Archive,Compressed,Encrypted,Hidden,Normal,ReadOnly,NotContentIndexed,SparseFile,System |
        ForEach-Object {

        [String]$filefullname = $_.DirectoryName + "\" + $_.BaseName + ".xlsx";
        if (Test-Path -PathType Leaf -Path $filefullname) {
                $ErrorActionPreference = 'Ignore';
                Remove-Item -Path $_.FullName
                $ErrorActionPreference = 'SilentlyContinue';
        } else {
          $EWB = $excel.Workbooks.Open($_.FullName)
          $EWB.SaveAs($_.DirectoryName + "\" + $_.BaseName + ".xlsx", 51)
          $_.DirectoryName + "\" + $_.BaseName + ".xlsx"
          $EWB.Close()
          Remove-Item -Path $_.FullName
        } 
    }
    Start-Sleep -s 3
}
$excel.Quit() # закрываем экземпляр Excel - это действие бессмысленно,
# т.к. при нормальной работе выхода из цикла не произойдет,
# а при аварийной остановке скрипта исполнение до этого места не дойдет

У меня несколько лет работает так.
Если есть идеи, как не гонять цикл, а отслеживать именно появление файла - было бы интересно посмотреть.

Вариант без висящего в памяти Excel

Код:

[String]$PathToAnalise = "D:\Обменка\Счета\"
For(  ;  ;  ) {
    Get-ChildItem -Path $PathToAnalise *.xls -File -Recurse -Attributes `
        Archive,Compressed,Encrypted,Hidden,Normal,ReadOnly,NotContentIndexed,SparseFile,System |
        ForEach-Object {
        [String]$filefullname = $_.DirectoryName + "\" + $_.BaseName + ".xlsx";
        if (Test-Path -PathType Leaf -Path $filefullname) {
                $ErrorActionPreference = 'Ignore';
                Remove-Item -Path $_.FullName
                $ErrorActionPreference = 'SilentlyContinue';
        } else {
          $excel = New-Object -ComObject Excel.Application
          $EWB = $excel.Workbooks.Open($_.FullName)
          $EWB.SaveAs($_.DirectoryName + "\" + $_.BaseName + ".xlsx", 51)
          $_.DirectoryName + "\" + $_.BaseName + ".xlsx"
          $EWB.Close()
          Remove-Item -Path $_.FullName
          $excel.Quit()
          Stop-Process -Name EXCEL

        } 
    }
    Start-Sleep -s 3
}

об этом скрипте у меня на сайте
в общем-то все итак видно, так что по ссылке ходить совсем не обязательно

DJ Mogarych 12-07-2025 20:43 3038858

antivan, в составе Офиса версии примерно с 2007 есть встроенный конвертер excelcnv.exe, и париться с ком-объектом Экселя уже не нужно, выше в теме было написано об этом.

Вот вариант с конвертацией и автоматическим удалением оригиналов.
Можно вставить в планировщик и запускать через определённые промежутки времени.
Проверки перед удалением оригиналов можно ещё навертеть какие угодно.
Код:

# Папка с файлами .xls
$folder = "C:\temp"
# Путь до конвертера
$converter = "C:\Program Files\Microsoft Office\root\Office16\excelcnv.exe"

# Запускать процесс только если в папке есть файлы .xls
if ($files = dir $folder -Include "*.xls" -Recurse) {
    # Конвертировать
    $files |% {
        & $converter -oice "$($_.FullName)" "$($_.FullName + "x")"
    }
    # Ожидание завершения процессов конвертации
    do {
        sleep 5
    } while (get-process excelcnv -ErrorAction SilentlyContinue)
    # Удалить оригинал, если есть соответствующий файл .xlsx и он не нулевого размера
    dir $folder -Include "*.xlsx" -Recurse |% {
        $xls = "$($_.DirectoryName)\$($_.basename).xls"
        if ($_.Length -ne 0 -and (Test-Path $xls)) {
            del $xls
        }
    }
}


antivan 12-07-2025 21:52 3038861

Цитата:

Цитата DJ Mogarych
в составе Офиса версии примерно с 2007 есть встроенный конвертер excelcnv.exe, и париться с ком-объектом Экселя уже не нужно, выше в теме было написано об этом. »

Видел, да. Но использование плохо документированной тулзы от майкрософта - моветон же. А скрипт с использованием ком-объекта - отработал несколько лет.

Никого ни к чему не призываю и не обязываю, естественно, просто таки созрел до публикации сайта, потихоньку выкладываю результаты своего "творчества". Не ахти что, но может кому-то будет полезно

YuS_2 13-07-2025 12:48 3038869

Цитата:

Цитата antivan
Если есть идеи, как не гонять цикл, а отслеживать именно появление файла - было бы интересно посмотреть. »

http://forum.oszone.net/post-2926058-11.html

antivan 14-07-2025 21:44 3038893

Цитата:

Цитата YuS_2
http://forum.oszone.net/post-2926058-11.html

Спасибо. Посмотрю

antivan 14-07-2025 22:36 3038894

Интересно. Но, похоже, проще гонять цикл с опросом, а задержкой можно и нагрузку регулировать


Время: 22:42.

Время: 22:42.
© OSzone.net 2001-