none
Posh 4. Задача удаления старых файлов. RRS feed

  • Вопрос

  • Доброго времени суток. Summon Kazun.

    Задача управления файлами по сроку истечения FSRM в 2012 R2 у меня так и не заработала (задачи запускаются, но игнорируют возраст файлов), хотя при точно такой же конфигурации в 2008 R2 все работало нормально, но это уже другая история. Поведение NTFS настроено на отслеживание последней даты доступа к файлу. Задачу пытаюсь решить с помощью скрипта. Вот он сам:

    #--Определим политку "возраста" файлов
    #--Получим текущую дату
    $Now = Get-Date
    #--Зададим количество дней с момента последнего доступа к файлу
    $Days = "5"
    #--Определим каталог с файлами
    $TargetFolder = "E:\Stat1"
    #--Определим допустимую старейшую дату последнего доступа к файлу
    $LastAccess = $Now.AddDays(-$Days)
    #--Найдем файлы, доступ к которым не производился после даты $LastAccess
    $Files = Get-Childitem $TargetFolder -Recurse | Where {$_.LastAccessTime -le "$LastAccess"}
    #--Включим модуль для использования Write-Zip командлета
    Import-Module pscx
    #--Текущая дата пойдет в название файла
    $zipdate = (Get-Date).tostring("dd.MM.yyyy")
    #--Формат имени архива
    $zipname = 'G:\tt\Stat1_' + $zipdate + '.zip'
    #--Поместим "устаревшие" файлы в архив
    $Files | Write-Zip -OutputPath $zipname -IncludeEmptyDirectories -EntryPathRoot $TargetFolder
    #--Удалим все устаревшие файлы
    Foreach ($File in $Files)
    	      {
    	      if ($File -ne $NULL)
    	          {
    	           write-host "Удаляю файл $File" -ForegroundColor "DarkRed"
    	           Remove-Item $File.FullName | out-null
    	           }
    	      else
             {
    	        Write-Host "Файлы для удаления отсутствуют." -foregroundcolor "Green"
    	        }
    	      }
    #--Найдем все пустые каталоги и удалим их, но некоторые мне нужно исключить (например emptyx2)
    #--Зададим корень поиска
    $SearchRoot = "E:\Stat1"
    #--Выберем пустые папки, но только не emptyx2, ее я удалять не хочу
    $EmptyFolders = Get-ChildItem -Path $SearchRoot -Directory -recurse | Where-Object {((Get-ChildItem -Path $_.FullName -Recurse) -eq $null) -and ($_.FullName -ne "E:\Stat1\Stat2\2gis\emptyx2")}
    #--Удалим эти пустые папки и выведем соответствующее сообщение:
    Foreach ($EmptyFolder in $EmptyFolders)
                            {
                            Write-Host "Удаляю пустой каталог $EmptyFolder" -ForegroundColor "DarkRed"
                            Remove-Item $EmptyFolder.FullName | out-null
                            }

    Вопросы:

    1. Каким образом лучше сделать отчет, на данный момент вывод после выполнения представляет собой нечто вроде:

    PS C:\Users\user\Desktop> C:\Users\nokogerra\Desktop\Delete by LastAccessTime.ps1
    
    Mode           LastWriteTime       Length Name                                                                                                                           
    ----           -------------       ------ ----                                                                                                                           
    -a---    16.12.2014    11:44    562163365 Stat1_16.12.2014.zip                                                                                                           
    Удаляю файл Data_Barnaul.dgdat
    ................................
    Удаляю пустой каталог stat3
    Удаляю пустой каталог emptyx

    Как сделать вывод в файл только строк с "удаляю бла-бла", ведь скрипт будет запускаться не вручную, а будет запускаться как задача из планировщика.

    2. Не относящийся конкретно к этой теме вопрос: использую модуль pscx (3.2, установка из msi) для write-zip, подключение модуля работает, но get-module -listavailable его не отображает. Почему?

    16 декабря 2014 г. 6:23

Ответы

  • 1) Write-Zip -OutputPath $zipname -IncludeEmptyDirectories -EntryPathRoot $TargetFolder

    Write-Zip .... | Out-Null

    Т.к. Write-Host выводит только на консоль, то сделать отдельную строчку или заменить Write-Host:

     "Удаляю файл $File" | Out-File result.txt -Append

    Или вывод сохранить в переменной и в конце скрипта выгружать :

    $sb = New-Object System.Text.StringBuilder
    $sb.AppendLine("Line1") | Out-Null
    $sb.AppendLine("Line2") | Out-Null
    $sb.ToString() | Out-File result.txt

    2) А куда установка производилась?

    PS > Get-Module pscx -ListAvailable
    
    
        Каталог: C:\Program Files (x86)\PowerShell Community Extensions\Pscx3
    
    
    ModuleType Version    Name                                ExportedCommands
    ---------- -------    ----                                ----------------
    Script     3.1.0.0    Pscx                                {Add-PathVariable

    16 декабря 2014 г. 6:34
    Отвечающий
  • 1) До Foreach определяем: $sb = New-Object System.Text.StringBuilder foreach { $sb.AppendLine ('Удаляю файл $File') } В конце скрипта: $sb.ToString() | Out-File result.txt 2) Поиск модулей берется из $env:PSModulePath . Get-Module -ListAvailable - Думаю, просто пропустили в общей массе PSCX) 3)

    Get-Childitem - Возвращает и файлы, и каталоги. Поэтому никакой неожиданности. но раньше каталоги не попадали в $Files. Теперь пришлось использовать параметр -File. - Повезло, чтобы избежать попадание каталогов, использовать параметр Get-ChildItem -File или в более младших версиях: Get-ChildItem .. | Where {!$_.PsIsContainer}


    16 декабря 2014 г. 10:13
    Отвечающий
  • 4) "Удаляю файл $($File.FullName)"

    5) Хорошо, можно передать параметр скрипту. Но вот передать параметр задаче без изменения ее нельзя.

    В начало скрипта:

    param(
    	$Days = "5",
    	$TargetFolder = "E:\Stat1"
    )

    Вызов(если параметры не указывать, будут браться по умолчанию):

    C:\scripts\script.ps1 -Days 10 -TargetFolder  "E:\New"

    В task scheduler изменить строчку Add arguments(optional),если только требуется изменить значения по умолчанию:

    -command "& 'c:\scripts\scripts.ps1' '15' 'E:\NewPath'"

    Если путь к скрипту содержит пробелы, то для экранирования использовать \.

    PowerShell "& \"C:\scripts (86)\scripts.ps1\" 15 'E:\NewPath'"

    16 декабря 2014 г. 11:09
    Отвечающий
  • Да можно указать и параметр, просто здесь опущены имена параметров. 15 - кол-во дней 'E:\NewPath' - путь

    Mandatory -  параметр требуется задавать обязательно

    [string[]] - означает, что параметр принимает массив строк.

    -vmName "a","b","c"

    [string] - тип строка

    -vmName "a"

    16 декабря 2014 г. 12:07
    Отвечающий
  • После амперсанда действительно нужен пробел? - Можно ставить, можно нет разницы нет.

    Варианты запуска, которые отработали(-noexit убрать если скрипт корректно запустился):

    -noprofile -noexit -file "C:\Users\kopilov\Desktop\testx.ps1" -TargetFolder "E:\Startx" -reportname "G:\Utilization\Reports\ratatax.txt"
    -noprofile -noexit "& 'C:\Users\kopilov\Desktop\testx.ps1' -TargetFolder 'E:\Startx' -reportname 'G:\Utilization\Reports\ratatax.txt'"

    ведь путь к скрипту заключен в кавычки зачем что-то экранировать, тем более вы поставили \ после амперсанда, а не внутри пути - Это не к PowerShell относится, а к task scheduler.

    вообще не особо понимаю принцип экранирования, например: - Начните читать документацию, чем разводить демагогию пустую.

    Get-Help about_Escape_Characters

    Escape characters are used to assign a special interpretation to
    the characters that follow it.
    
    In Windows PowerShell, the escape character is the backtick (`), also
    called the grave accent (ASCII 96). The escape character can be used
    to indicate a literal, to indicate line continuation, and to indicate
    special characters.
    The following special characters are recognized by Windows PowerShell:
    
        `0    Null
        `a    Alert
        `b    Backspace
        `f    Form feed
        `n    New line
        `r    Carriage return
        `t    Horizontal tab
        `v    Vertical tab



    17 декабря 2014 г. 5:39
    Отвечающий

Все ответы

  • 1) Write-Zip -OutputPath $zipname -IncludeEmptyDirectories -EntryPathRoot $TargetFolder

    Write-Zip .... | Out-Null

    Т.к. Write-Host выводит только на консоль, то сделать отдельную строчку или заменить Write-Host:

     "Удаляю файл $File" | Out-File result.txt -Append

    Или вывод сохранить в переменной и в конце скрипта выгружать :

    $sb = New-Object System.Text.StringBuilder
    $sb.AppendLine("Line1") | Out-Null
    $sb.AppendLine("Line2") | Out-Null
    $sb.ToString() | Out-File result.txt

    2) А куда установка производилась?

    PS > Get-Module pscx -ListAvailable
    
    
        Каталог: C:\Program Files (x86)\PowerShell Community Extensions\Pscx3
    
    
    ModuleType Version    Name                                ExportedCommands
    ---------- -------    ----                                ----------------
    Script     3.1.0.0    Pscx                                {Add-PathVariable

    16 декабря 2014 г. 6:34
    Отвечающий
  • Спасибо за ответ.

    1. Вариант с "Удаляю файл $File" | Out-File result.txt -Append сработал хорошо. А по поводу сохранения вывода в переменной не совсем понял, как это будет выглядеть, можно также поместить в цикл и пополнять примерно таким образом: 

    foreach (.....)
            {....
             $sb.AppendLine ('Удаляю файл $File')
    ...
            }
    ?

    2. MSI не спрашивал куда установить, но установил сюда: 

    "C:\Program Files (x86)\PowerShell Community Extensions\Pscx3", если указать в Get-Module имя модуля, то да, показывает такую же картину как у вас (только версия 3.2). Т.е. либо помещать в %windir%\system32\windowspowers...., либо с указанием модуля?

    3. Можете подсказать с еще одним вопросом:

    для теста в политике "возраста" файлов сделал такие изменения: вместо дней использовал минуты:

    $LastAccess = $now.addminutes(-$minutes), чтобы например удалять файлы, к которым не обращались 5 минут, и ВНЕЗАПНО на этапе удаления файлов начали удаляться и каталоги (элементы $File из $Files), чего не было при использовании дней, а не минут:

    $Files = Get-Childitem $TargetFolder -Recurse | Where {$_.LastAccessTime -le "$LastAccess"}

    Я обращал внимание, что у DIR в Get-ChildItem есть атрибут lastaccesstime, но раньше каталоги не попадали в $Files. Теперь пришлось использовать параметр -File. Почему так?

    16 декабря 2014 г. 10:00
  • 1) До Foreach определяем: $sb = New-Object System.Text.StringBuilder foreach { $sb.AppendLine ('Удаляю файл $File') } В конце скрипта: $sb.ToString() | Out-File result.txt 2) Поиск модулей берется из $env:PSModulePath . Get-Module -ListAvailable - Думаю, просто пропустили в общей массе PSCX) 3)

    Get-Childitem - Возвращает и файлы, и каталоги. Поэтому никакой неожиданности. но раньше каталоги не попадали в $Files. Теперь пришлось использовать параметр -File. - Повезло, чтобы избежать попадание каталогов, использовать параметр Get-ChildItem -File или в более младших версиях: Get-ChildItem .. | Where {!$_.PsIsContainer}


    16 декабря 2014 г. 10:13
    Отвечающий
  • 1. Я это имел в виду, спасибо.

    2. Магия какая-то:

    PS C:\Windows\system32> Get-Module -ListAvailable | where {$_.name -like '*pscx*'}
    
    
        Каталог: C:\Program Files (x86)\PowerShell Community Extensions\Pscx3
    
    
    ModuleType Version    Name                                ExportedCommands
    ---------- -------    ----                                ----------------
    Script     3.2.0.0    Pscx                                {Add-PathVariable, Clear-MSMQueue, ConvertFrom-Base64, Con...

    Но если выполнить просто get-module -listavailable, то модуля в списке нет 100%, даже 200. Ну и ладно, работает.

    3. Я и использовал -File, просто не понял почему раньше (без -File) каталоги не попадали в эту переменную.

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

    4. Чтобы выводить в отчет полное имя файла (короткое не информативное) использую такую схему:

    Foreach ($File in $Files)
    	      {
    	      if ($File -ne $NULL)
    	          {
    	           $F=$File.FullName
                       "Удаляю файл $F" | Out-File $reportname -Append
    ..................

    Очевидно, это какая-то неправильная схема, хотя и рабочая, но "Удаляю файл $File.fullname" не работает, как и с одинраными кавычками. Может есть способ проще?

    5. Я задаю переменные в скрипте (по сути 1-2 переменных), уверен их можно задать в качестве параметров к запуску скрипта (а задаче планировщика например), но как не знаю, и толком запрос сформулировать не могу.

    16 декабря 2014 г. 10:57
  • 4) "Удаляю файл $($File.FullName)"

    5) Хорошо, можно передать параметр скрипту. Но вот передать параметр задаче без изменения ее нельзя.

    В начало скрипта:

    param(
    	$Days = "5",
    	$TargetFolder = "E:\Stat1"
    )

    Вызов(если параметры не указывать, будут браться по умолчанию):

    C:\scripts\script.ps1 -Days 10 -TargetFolder  "E:\New"

    В task scheduler изменить строчку Add arguments(optional),если только требуется изменить значения по умолчанию:

    -command "& 'c:\scripts\scripts.ps1' '15' 'E:\NewPath'"

    Если путь к скрипту содержит пробелы, то для экранирования использовать \.

    PowerShell "& \"C:\scripts (86)\scripts.ps1\" 15 'E:\NewPath'"

    16 декабря 2014 г. 11:09
    Отвечающий
  • 4. Спасибо.

    5. Не совсем понял, т.е. при вызове скрипта из командной строки можно указать таким образом: ... -Days 10, но для задачи только таким образом: PowerShell "& \"C:\scripts (86)\scripts.ps1\" 15 'E:\NewPath'"? Нельзя указать также -Days ...?

    Здесь (это скрипт PowerCli - грубо говоря надстройка над Powershell для VMware vSphere, синтаксис тот же самый, командлеты другие) в аргументах к задаче используется запись как раз такого вида -days ...

    Только не понятна запись эта:

    param(
        [parameter(Mandatory = $true)]
        [string[]]$vCenter,
        [parameter(Mandatory = $true)]
        [string[]]$vmName
    )
    что за string и что за Mandatory.


    16 декабря 2014 г. 11:35
  • Да можно указать и параметр, просто здесь опущены имена параметров. 15 - кол-во дней 'E:\NewPath' - путь

    Mandatory -  параметр требуется задавать обязательно

    [string[]] - означает, что параметр принимает массив строк.

    -vmName "a","b","c"

    [string] - тип строка

    -vmName "a"

    16 декабря 2014 г. 12:07
    Отвечающий
  • Спасибо за ответ. В задаче не работает никак.

    Тестовый скрипт:

    param (
        [parameter(Mandatory = $true)]
        $TargetFolder,
        [parameter(Mandatory = $true)]
        $reportname
          )
    
    $Files = Get-ChildItem $TargetFolder -Recurse
    foreach ($file in $Files)
        {
    	"Удаляю файл $($file.Fullname)" | Out-File $reportname -Append
    	}

    При запуске из Powershell работает нормально:

    C:\Users\kopilov\Desktop\testx.ps1 -TargetFolder "E:\Startx" -reportname "G:\Utilization\Report\tatata.txt"

    Но при запуске задачи - не создает отчет, хотя задача числится выполненной успешно. При настройке задачи указываю:

    Программа или сценарий:C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe

    Аргументы пробовал разные

    a.

    PowerShell "& 'C:\Users\kopilov\Desktop\testx.ps1' -TargetFolder 'E:\Startx' -reportname 'G:\Utilization\Reports\ratatax.txt'"

    b.

    PowerShell & "C:\Users\kopilov\Desktop\testx.ps1" -TargetFolder "E:\Startx" -reportname "G:\Utilization\Reports\ratatax.txt"

    c.

    PowerShell "& \'C:\Users\kopilov\Desktop\testx.ps1\' -TargetFolder 'E:\Startx' -reportname 'G:\Utilization\Reports\ratatax.txt'"

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

    И честно говоря не понял что значит "Если путь к скрипту содержит пробелы, то для экранирования использовать \.", ведь путь к скрипту заключен в кавычки зачем что-то экранировать, тем более вы поставили \ после амперсанда, а не внутри пути. После амперсанда действительно нужен пробел?

    P.S. вообще не особо понимаю принцип экранирования, например:

    $c=10

    "$c" выведет 10 - понятно

    "`$c" выведет $c - вроде логично, т.к. специальный знак переменной $ экранирован (т.е. как я понимаю игнорируется)

    а вот другой специальный знак "`t$c" при экранировании наоборот срабатывает. И мы получим:

    10

    Т.е. если экранировать $, то этот знак не обрабатывается как специальный, а если экранировать табуляцию (t), то этот знак наоборот будет обрабатываться как специальный. Логика не ясна.


    17 декабря 2014 г. 4:54
  • После амперсанда действительно нужен пробел? - Можно ставить, можно нет разницы нет.

    Варианты запуска, которые отработали(-noexit убрать если скрипт корректно запустился):

    -noprofile -noexit -file "C:\Users\kopilov\Desktop\testx.ps1" -TargetFolder "E:\Startx" -reportname "G:\Utilization\Reports\ratatax.txt"
    -noprofile -noexit "& 'C:\Users\kopilov\Desktop\testx.ps1' -TargetFolder 'E:\Startx' -reportname 'G:\Utilization\Reports\ratatax.txt'"

    ведь путь к скрипту заключен в кавычки зачем что-то экранировать, тем более вы поставили \ после амперсанда, а не внутри пути - Это не к PowerShell относится, а к task scheduler.

    вообще не особо понимаю принцип экранирования, например: - Начните читать документацию, чем разводить демагогию пустую.

    Get-Help about_Escape_Characters

    Escape characters are used to assign a special interpretation to
    the characters that follow it.
    
    In Windows PowerShell, the escape character is the backtick (`), also
    called the grave accent (ASCII 96). The escape character can be used
    to indicate a literal, to indicate line continuation, and to indicate
    special characters.
    The following special characters are recognized by Windows PowerShell:
    
        `0    Null
        `a    Alert
        `b    Backspace
        `f    Form feed
        `n    New line
        `r    Carriage return
        `t    Horizontal tab
        `v    Vertical tab



    17 декабря 2014 г. 5:39
    Отвечающий
  • Спасибо, оба варианта работают.

    Дошло, программа Powershell (я думал необходимо указывать полный путь), не понятно почему в первом варианте не потребовался оператор вызова, разве он не необходим чтобы последующее выражение определить как команду?

    По поводу экранирования - да я не читал документацию, я читал об экранировании на блоге msdn и osp и там был пример с $, которого пусть и нет в списке, но он же является специальным символом. Поэтому противоположная реакция ` на эти символы мне показалась странной.

    17 декабря 2014 г. 6:00