- PVSM.RU - https://www.pvsm.ru -
[1] Иногда нужно быстро распечатать много картинок с котиками документов, а открывать для этого каждый файл совсем не хочется. Первым делом напрашивается использование контекстного меню проводника, но у этого способа есть свои ограничения и нюансы. Поэтому пришлось искать альтернативу. За подробностями — прошу под кат.
Тема пакетной печати не раз освещалась в трудах великих учёных интернет-статьях. Например, в этой [2] и этой [3].
Мы же начнем с выяснения того, какой функционал нужен конечным пользователям. В результате общения с коллегами получися такой список:
Пожалуй, самым простым и очевидным видится печать из контекстного меню проводника, о чем можно почитать тут [4] и здесь [5]. По второй ссылке дана информация по удалению пункта «Печать» для определенных типов файлов, но сообразительный читатель легко поймет из нее, как, наоборот, можно добавить недостающее.
Но у такого способа есть, как минимум, два недостатка:
Первый недостаток легко устраним [6] твиком реестра. Для решения второго есть рекомендации [7] в виде танцев с бубном, но в нашей среде боги суровы и эти обряды не помогли.
Есть готовые сторонние решения (ссылки на статьи с информацией о них даны выше). Но при коммерческом использовании за эти продукты придется заплатить, к тому же всегда приятно забить элегантный костыль и изобрести очередной велосипед сделать что-то своими руками.
Примечание. Чтобы не переводить бумагу, на этапе подготовки и тестирования скрипта удобно использовать виртуальный принтер. Меня устроил штатный Microsoft XPS Document Writer, но есть еще PDF24 Creator [8], doPDF [9], CutePDF Writer [10] — как говорится, на вкус и цвет…
В качестве языка был выбран PowerShell. В базовой комплектации скрипт выглядит так:
$FolderToPrint = "\servershareFolder"
$FileMask = "*.xml"
$FolderToPrint | Get-ChildItem -File -Filter $FileMask | Sort-Object Name | ForEach-Object {
Write-Output ("Печать файла `"" + $_.FullName + "`"")
Start-Process -FilePath notepad -ArgumentList ("/P `"" + $_.FullName + "`"") -Wait
}
Печать выполняется средствами штатного блокнота Windows (чтоб не простаивал без дела).
Как видно из 3-й строки, сортировка в примере происходит по имени файла (Name). Вместо этого можно взять за основу размер (Length) или дату изменения (LastWriteTime). Если вам требуется что-то более экзотичное, можно зайти сюда [11].
Для сортировки в обратном порядке у командлета Sort-Object [12] есть ключ -Descending.
В этом варианте печать идет на принтер по умолчанию, и нас такое поведение устроило. Если же нужно печатать на принтер, отличный от дефолтного, у блокнота есть параметр /PT.
Соответственно, код примет следующий вид:
<...>
$PrinterName = "\server2Network Printer"
<...>
Start-Process -FilePath notepad -ArgumentList ("/PT `"$PrinterName`" `"" + $_.FullName + "`"") -Wait
<...>
Аналогично вместо блокнота можно поэксплуатировать любую другую программу в зависимости от того, какой формат файлов нужно печатать. Главное — чтобы она поддерживала печать через интерфейс командной строки.
Примечание. Если будете приручать Adobe Reader, имейте в виду этот [13] старый баг. В нашем окружении он все еще проявляется, возможно, вам повезет больше. А еще есть хорошая статья [14], посвященная печати PDF из PowerShell.
Если же вам на выходе нужен только «голый» текст из обычного текстовика, то 5-я строка варианта 0 примет такой вид:
Get-Content $_.FullName | Out-Printer -Name $PrinterName
Для печати на дефолтный принтер параметр -Name нужно опустить.
Для нашей задачи требовалась печать файлов из нескольких расположений. Немного дополнив вариант 0, получаем
$FolderToPrint = @(
"\server1shareFolder1",
"\server1shareFolder2",
"\server1shareFolder3"
)
$FileMask = "*.xml"
$ErrorActionPreference = "Stop"
Try {
$FolderToPrint | Get-ChildItem -File -Filter $FileMask | Sort-Object Name | ForEach-Object {
Write-Output ("Печать файла `"" + $_.FullName + "`"")
Start-Process -FilePath notepad -ArgumentList ("/P `"" + $_.FullName + "`"") -Wait
}
}
Catch {
Write-Host "При выполнении операции возникла ошибка:"
Write-Host $Error[0] -ForegroundColor Red
Read-Host "Нажмите ENTER для завершения"
}
Для приличия добавлена функция обработки исключений. И в случае, если, например, папка, из которой печатаются файлы, стала недоступной, то выполнение печати прервется и пользователю будет выведено соответствующее уведомление. Кстати, замечено, что блокнот возвращает в exit-коде 0 даже при попытке распечатать несуществующий/недоступный файл, но в GUI при этом ругается.
Опробовав вариант 1, пользователи попросили дать возможность выбора папки и конкретных файлов в ней, поэтому было добавлено немного интерактивности в виде диалогового окна выбора файлов. Так появился
Add-Type -AssemblyName System.Windows.Forms | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.InitialDirectory = "\servershare"
$OpenFileDialog.Multiselect = $True
$OpenFileDialog.Filter = "XML-файлы (*.xml)|*.xml|Все файлы (*.*)|*.*"
$OpenFileDialog.ShowHelp = $true
$OpenFileDialog.ShowDialog() | Out-Null
$FilesToPrint = $OpenFileDialog.FileNames | Sort-Object
ForEach ($FullFileName in $FilesToPrint) {
Write-Output "Печать файла `"$FullFileName`""
Start-Process -FilePath notepad -ArgumentList ("/P `"$FullFileName`"") -Wait
}
Теперь при запуске получаем привычное окно проводника Windows с удобным выбором нужных файлов:
Подробнее о работе с диалоговым окном открытия файлов можно почитать в официальной документации [15], а кто хочет узнать больше про GUI-зацию PowerShell, легко найдет много материала в сети, есть даже онлайн-конструктор форм [16].
Обработка исключений во втором варианте была убрана, т.к. интерактивное информирование пользователя было отдано на откуп проводнику и блокноту.
При запуске кода из ISE диалоговое окно выбора файлов выводится на заднем плане (Ctrl+Tab в помощь), но из командной строки все работает как положено. Также обратите внимание, что свойство ShowHelp должно быть $true, чтобы обойти этот [17] баг.
Еще хотелось бы обратить внимание на свойство InitialDirectory. Кэп подсказывает, что оно определяет путь к папке, которая будет выбрана по умолчанию при запуске скрипта. Но, учитывая, что проводник «запоминает» последнее выбранное расположение, которое было указано пользователем в диалоге выбора файлов, InitialDirectory может пригодиться только при первом запуске скрипта.
Вариант 2 полностью подошел нашим пользователям, поэтому на нем мы и остановились. Но если вам нужен вариант с дамами и преферансом интерактивностью и сортировкой, отличной от имени (например, по дате изменения), это тоже реализуемо. Получаем
Add-Type -AssemblyName System.Windows.Forms | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.InitialDirectory = "\servershare"
$OpenFileDialog.Multiselect = $True
$OpenFileDialog.Filter = "XML-файлы (*.xml)|*.xml|Все файлы (*.*)|*.*"
$OpenFileDialog.ShowHelp = $true
$OpenFileDialog.ShowDialog() | Out-Null
$SelectedFiles = $OpenFileDialog.FileNames
#Если ничего не выбрано, завершаем работу
If (!($SelectedFiles)) {
Break
}
#На основании полного имени выбранного файла определяем выбранную папку
$SelectedDir = (Split-Path -Parent $OpenFileDialog.FileName)
#Получаем список всех файлов в выбранной папке
$FilesToPrint = Get-ChildItem -Path $SelectedDir -Force |
#отбираем только те из них, которые были выбраны в диалоговом окне
Where-Object {$_.FullName -in $OpenFileDialog.FileNames} |
#и сортируем
Sort-Object -Property LastWriteTime
ForEach ($File in $FilesToPrint) {
$FullFileName = $File.FullName
Write-Output "Печать файла `"$FullFileName`""
Start-Process -FilePath notepad -ArgumentList ("/P `"$FullFileName`"") -Wait
}
Т.к. из объекта $OpenFileDialog нельзя напрямую извлечь такие параметры, как размер или дату создания файла, то мы с помощью командлета Get-ChildItem [18] получаем список всех файлов в папке, выбранной пользователем, а потом оставляем только те из них, которые были выбраны пользователем, и сортируем их в нужном нам виде.
Убедившись, что всё работает как надо, кладем скрипт в сетевую папку и выводим пользователям ярлык на рабочий стол.
А чтобы наш маленький беззащитный скрипт не обижали злые Execution Policies [19], прячем его в такую скорлупу:
powershell.exe -NoLogo -ExecutionPolicy Bypass -File "\servershareScriptsBulkPrint.ps1"
Или можно обернуть [20] в теплый ламповый батник.
Среди прочего, в корпоративной среде запуску скрипта могут мешать суровые Software Restriction Policies [21] и безжалостный AppLocker [21], а также другое защитное ПО, но это уже выходит за рамки статьи.
Можно добавить лоска, установив красивый значок для ярлыка. Я выбрал такой:
Если пользователей нашего скрипта много, можно массово раздать ярлык с помощью предпочтений групповой политики [22].

Такое бывает, если выкатить без предварительного тестирования.
А у нас будет вот так:

И крамольная мысль напоследок: а что, если подумать в другом направлении и вместо всего описанного выше пообщаться с начальством и перестроить рабочий процесс?

Автор: perlestius
Источник [23]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/windows/309765
Ссылки в тексте:
[1] Image: https://habr.com/post/441384/
[2] этой: https://www.makeuseof.com/tag/5-ways-to-print-folder-and-directory-contents-in-windows/
[3] этой: https://www.fcoder.com/directions/batch-printing-software
[4] тут: http://forum.oszone.net/thread-239727.html
[5] здесь: https://serverfault.com/questions/436536/disable-right-click-file-printing-in-windows
[6] легко устраним: https://www.thewindowsclub.com/print-more-than-15-files-windows/amp
[7] рекомендации: https://wordribbon.tips.net/T013256_Printing_Multiple_Documents_in_a_Sorted_Order.html
[8] PDF24 Creator: https://ru.pdf24.org/creator.html
[9] doPDF: http://www.dopdf.com/ru/download.php
[10] CutePDF Writer: http://www.cutepdf.com/products/cutepdf/writer.asp
[11] сюда: https://devblogs.microsoft.com/scripting/use-a-powershell-cmdlet-to-work-with-file-attributes/
[12] Sort-Object: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/sort-object?view=powershell-6
[13] этот: https://forums.adobe.com/thread/1042132
[14] хорошая статья: https://gregcaporale.wordpress.com/2012/01/18/powershell-to-print-files-automatically/
[15] официальной документации: https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.openfiledialog?view=netframework-4.7.2
[16] онлайн-конструктор форм: https://poshgui.com/
[17] этот: https://stackoverflow.com/questions/20949733/open-file-dialog-showing-help-button
[18] Get-ChildItem: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-6
[19] Execution Policies: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-6
[20] обернуть: https://www.sevecek.com/EnglishPages/Lists/Posts/Post.aspx?ID=98
[21] Software Restriction Policies: https://docs.microsoft.com/ru-ru/windows-server/identity/software-restriction-policies/software-restriction-policies
[22] предпочтений групповой политики: http://www.oszone.net/15787/gp-pref-6
[23] Источник: https://habr.com/ru/post/441384/?utm_campaign=441384
Нажмите здесь для печати.