locked
[POWERSHELL] Sort Dictionary RRS feed

  • Domanda

  • Buongiorno!

    Oggi ho un problemino un po' troppo complicato per me:

    $Mat=@{Nomi="Alberto, Nicole, Giulio, Lucia";
    Anni="28,23,32,35"}
    

    volevo ordinare questa "matrice" organizzandola per valori per ottenere

    $Mat=@{Nomi="Nicole, Alberto, Giulio, Lucia";
    Anni="23,28,32,35"}

    tuttavia non so come si fa...
    Soprattutto se al posto di avere nomi ho percorsi di file...

    Grazie

    A

    lunedì 2 dicembre 2013 11:07

Risposte

Tutte le risposte



  • La tua matrice risulta formata da due soli elementi uno è Nomi che vale "Nicole, Alberto, Giulio, Lucia"  e Anni che  è uguale a "23,28,32,35" e non da quattro coppie di elementi

    Working with Hash Tables

    http://blogs.technet.com/b/heyscriptingguy/archive/2011/07/06/use-a-powershell-hash-table-to-simplify-file-backups.aspx

    cls
    $Mat=@{'Alberto'='28';
           'Nicole'='23'; 
           'Giulio'='32';
           'Lucia'='35'}
     'Non ordinato:'
     $mat
     'Ordinato per valore:'
     $mat.GetEnumerator() | Sort-Object value
     'Ordinato per chiave:'
     $mat.GetEnumerator() | Sort-Object key
     'singolo elemento:'
     $nome='nicole'
     "anni di $nome $($mat."$nome")"





    Gastone Canali >http://www.armadillo.it

    Se alcuni post rispondono al tuo quesito(non necessariamente i miei), ricorda di contrassegnarli come risposta e non dimenticare di contrassegnare anche i post utili. GRAZIE! Ricorda di dare un occhio ai link Click Here andHere

    lunedì 2 dicembre 2013 12:46
  • La tua matrice risulta formata da due soli elementi uno è Nomi che vale "Nicole, Alberto, Giulio, Lucia"  e Anni che  è uguale a "23,28,32,35" e non da quattro coppie di elementi

    In effetti sono a conoscenza dell'utilizzo scorretto che sto facendo, infatti dopo una ricerca su internet l'ho capito. Il mio problema ha origine qui:

    $Search= "C:\Test"
    
    $drives=Get-ChildItem $Search -Recurse -Force|
    foreach{
        if($_.Length -gt 100MB){
            $Mb=($_.Length)/1MB 
            $Sol= $Mb,"Mb" -join(" ")
            $Dic=@{
                Name=split-path $_.fullname -leaf
                Percorso=$_.fullname
                Dimensione=$sol
            }
            New-Object PSObject -Property $Dic
        }
    }

    ovvero dalla necessità di creare un ordinamento nella "matrice" (matrice che poi viene convertita in una tabella in html, ma questa parte, già fatta, viene dopo).

    Ho pensato a una soluzione del genere:

    $Mat=@{N1=@(Vi);N2=@(Vj)}

    Ma devo ancora svilupparla, anche perché sono alle prime armi(Difatti non so ancora se c'è differenza tra $mat.N1 e $mat["N1"] per chiarirci),  e voglio vedere se ci sono anche altre strade...

    Grazie

    A

    lunedì 2 dicembre 2013 13:04
  • Senza conoscere powershell ti complichi la vita, ecco come fare ciò che vuoi:

    lo script sotto esegue le operazioni che vuoi ...per le strade alternative leggi i libri sotto

    elencare il file maggiori di 100 MB, ordinarli per dimensione e infine creare un generico file html

    gci -path c:\ -r -fo | ? { ($_.Length /1MB) -gt 100 } | select-object name, length|Sort-Object -Property length | ConvertTo-Html | out-file c:\temp\file-oltre-100MB.htm # Apre il file html appena creato Invoke-Expression c:\temp\file-oltre-100MB.htm

    Gugolando ho trovato questi (mastering powershell da quando è uscito è sempre com me)

    http://powershell.com/Mastering-PowerShell.pdf

    http://www.computerperformance.co.uk/Sampler/Get%20started%20with%20PowerShell.pdf

    http://eddiejackson.net/web_documents/The_Administrator_Crash_Course_Windows_PowerShell%20v2.pdf

    http://gegeek.com/documents/eBooks/Microsoft%20Windows%20Powershell%20Step%20by%20Step.pdf



    Gastone Canali >http://www.armadillo.it

    Se alcuni post rispondono al tuo quesito(non necessariamente i miei), ricorda di contrassegnarli come risposta e non dimenticare di contrassegnare anche i post utili. GRAZIE! Ricorda di dare un occhio ai link Click Here andHere



    • Modificato GastoneCanali lunedì 2 dicembre 2013 19:50
    • Proposto come risposta GastoneCanali lunedì 2 dicembre 2013 20:58
    • Contrassegnato come risposta Albiz martedì 3 dicembre 2013 09:25
    lunedì 2 dicembre 2013 19:00
  • Senza conoscere powershell ti complichi la vita


    Sto tentando pian pianino di imparare; visto che sui linguaggi di programmazione come C++, Python etc ho sempre bazzicato, penso di poter riuscire ad imparare anche powershell e di risolvere i problemi che man mano si presentano.

    Quanto ai libri, ho iniziato dalle cose basilari come: http://www.techrepublic.com/blog/10-things/10-fundamental-concepts-for-powershell-scripting/ e pian piano sto tentando di migliorare, quindi grazie per i 4 pdf sono molto utili, immagino possano darmi molte risposte e far migliorare la logica dei miei script (il che significa che ho letto i post del link, ho capito cosa intendi e difatti sotto viene trattato).

    La mia situazione attuale per il problema file>100Mb:

    $Today = Get-Date -Format "dd-MMM"
    $Search= "C:\Test"
    
    $drives = Get-ChildItem $Search -Recurse -Force |
    Where-Object { $_.Length -gt 100MB } |
    Sort-Object -Property Length |
    Select-Object -Property @(
        'Name'
        @{ Name = 'Percorso'; Expression = { $_.FullName } }
        @{ Name = 'Dimensione'; Expression = { "$($_.Length / 1MB) MB" } }
    )
    
    $head = @'
    <Title>Large Files Report</Title>
    <style>
    body 
    { 
     background-color:#FFFFFF;
     font-family:Verdana;
     font-size:12pt; 
    }
    td, th 
    { 
     border:1px solid blue; 
     border-collapse:collapse; 
    }
    th 
    {
     color:white;
     background-color:green; 
    }
    table, tr, td, th { padding: 5px; margin: 0px }
    table { margin-left:50px; }
    </style>
    '@
    
    #create an xml document from the HTML fragment
    [xml]$html = $drives | ConvertTo-Html -fragment
    
    
    #check each row, skipping the TH header row
    for ($i=1;$i -le $html.table.tr.count-1;$i++) {
      $class = $html.CreateAttribute("class")
    }
    
    #create the final report from the innerxml which should be html code
    $body = @"
    <H1>Large Files Report</H1>
    $($html.innerxml)
    "@
    
    #put it all together
    $Log = "Large Files Report" + $Today
    
    ConvertTo-HTML -head $head  -PostContent "<br><i>$(Get-date)</i>" -body $body | 
    Out-File "C:\Log\$Log.html" -Encoding ascii
    
    Invoke-Item "C:\Log\$Log.html"

    Grazie ad alcune persone in questo forum, tutta la parte di ricerca dei file si è ridotta a poche righe (prima appunto era più lunga, senza o quasi senza cmdlet e logica) nelle quali viene salvato $drives, file che mi serve per creare la tabella in html; premesso che html non è un linguaggio a me conosciuto, ma spero in futuro di poterci capire qualcosa, tutta la parte di creazione di questa tabella è stata copiata da un altro forum.

    Ovviamente non è che mi sono limitato a fare taglia -incolla, (altrimenti che miglioramento è?) ho provato a capire come funzionava e perché era costruito in tale maniera (quindi un po' di tempo in get-help e Google l'ho speso) ma "Rome wasn't built in a day"(cit. Morcheeba).

    In merito al link, lo script per spostare le cartelle vuote al momento è questo:

    $Move=@() 
    #$Et=("0*","1*","8*","9*","Scansioni")
    $Today = Get-Date -Format "dd-MMM"
    $Folder= "C:\Estinto\$Today\EmptyFolders"
    $Search= "C:\Test"
    
    if(!(Test-Path $Folder)){
        New-Item -Path $Folder -ItemType directory
    }
    
    Get-ChildItem $Search -Recurse | 
    Where-Object {(Get-ChildItem -Path $_.FullName) -eq $null -and $_.PSISContainer} | 
    foreach($_){ 
        $Sav=Split-Path($_) -leaf 
        if($Sav -match "^[0-1-8-9].*"){"Can't Move Boss"}
        elseif($Sav -match "Scansion"){"Can't Move Boss"}
        else{
            $Mem=$_
            $Move=$Move+$Mem.fullname 
            $Divide=$Mem.Parent.fullname -split('')
            $Impera=""
            foreach($count in $divide){
                if($count -match ':'){
                    $count="$" 
                }
                $Impera += $count
            }
            $Dest=$Folder,$impera -join('\')
            if(!(Test-Path $Dest)){
                New-Item -Path $Dest -ItemType directory
            }
            
       
            Move-Item -Path $Mem.FullName -Destination $Dest -force 
        }
    }
    
    $Move= $Move | sort{Split-Path($_) -leaf}
    
    $drives = foreach ($path in $Move) {
        $prophash = @{
            Nome= split-path($path) -leaf
            Percorso = $path
        }
    
        #create a new object from the property hash
        New-Object PSObject -Property $prophash
        }

    Non è variato gran che dall'ultima volta, ma ora grazie al tuo post posso finalmente modificarlo.

    Non è che non conosca la logica che tu hai usato, è solo che non ho mai capito fino in fondo come applicarla, ad esempio:

    $Today= Get-Date -Format "dd-MMM"
    
    function Get-Double {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory=$true,
                       Position=0,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true)]
            [System.String]
            [Alias('PSPath')]
            $Path
        )
    
        if ((Test-Path -Path $Path) -eq $false) {
            throw New-Object System.IO.FileNotFoundException($Path)
        }
    
        try {
            $item = Get-Item -Path $Path -Force -ErrorAction Stop
        } 
        catch {
            throw
        }
    
        if ($item -isnot [System.IO.FileInfo]) {
            throw New-Object System.ArgumentException('Specified path must refer to a File object.','Path')
        }
    
        try {
            $bytes = [System.IO.File]::ReadAllBytes($item.FullName)
        } catch {
            throw
        }
    
        $md5Hash = [System.Security.Cryptography.MD5]::Create()
        $hashBytes = $md5Hash.ComputeHash($bytes)
    
        $sb = New-Object System.Text.StringBuilder
    
        foreach ($byte in $hashBytes) {
            $sb.Append($byte.ToString("x2")) | Out-Null
        }
    
        Write-Output $sb.ToString()
    }
    
    $Files = @{}
    $Search = "C:\Test\"
    
    Get-ChildItem $Search -Force -Recurse | 
    Where-Object { $_ -is [System.IO.FileInfo] } |
    ForEach-Object {
        try {
            $hash = $_ | Get-Double
        } catch {
            Write-Error -ErrorRecord $_
            return
        }
    
        if ($Files.ContainsKey($hash) -eq $false) {
            $Files[$hash] = New-Object 'System.Collections.Generic.List[System.String]'
        } else {
            # I'm just identifying duplicates by adding all their names to a list.
            # Keep in mind that if two files have identical hashes, they could have
            # different contents; it's possible for two different inputs to produce the same
            # hash code.
        }
    
        $Files[$hash].Add($_.FullName)
    }
    
    $Data=@()
    foreach ($entry in $Files.GetEnumerator()) {
        if ($entry.Value.Count -gt 1) {
            $Data+=$entry.Value
            $data+=" "
        }
    }
    $drives = foreach ($path in $Data) {
        $time=""
        if(!($path -eq " ")){
            $time=(Get-ItemProperty $path).lastAccessTime
        }
        $prophash = @{
            Nome= split-path $path -leaf
            Percorso = $path
            LastAccess=$time
        }
    
        New-Object PSObject -Property $prophash
    }

    qui è stata creata una funzione, per citare quel che so di C++, fuori dal main e poi è stata chiamata.

    Comprendo perfettamente la differenza, variabili locali, variabili globali, funzioni interne o esterne al main, tuttavia la mia preoccupazione principale è stata creare degli script funzionanti, senza badare a logiche, tempi di esecuzione, allocamenti di memoria etc...

    In questa settimana, dato che gli script, per quanto mal scritti, il loro dovere lo fanno, sto cercando di migliorare logica, struttura etc.. Per far ciò ovviamente in primis vedrò di migliorare le conoscenze usando i 4 pdf che gentilmente mi hai passato, quindi vedrò di applicarle agli script creati.

    Già che è stato scritto tanto a questo punto inserisco anche gli altri due script (così almeno per una volta tutti e cinque vengono presentati XD):

    Ricerca di file eseguibili (WIP):

    $Move=@()
    $Today = Get-Date -Format "dd-MMM"
    $Search= "C:\Test"
    $Type=@('*.bat','*.cmd','*.com','*.cpl','*.exe','*.inf','*.js','*.jse','*.msh','*.msi','*.msp','*.ocx','*.pif','*.pl','*.ps1','*.scr','*.vb','*.vbs','*.wsf','*.wsh')
    
    $drives = Get-ChildItem $Search -Recurse -Force |
    Where-Object { $_.Extension -match $Type} |
    Sort-Object -Property Name |
    Select-Object -Property @(
        'Name'
        @{ Name = 'Percorso'; Expression = { $_.FullName } }
        @{ Name = 'Dimensione'; Expression = { "$($_.Length / 1MB) MB" } }
    )

    Dulcis in fundo: Spostamento file temporanei:

    $Move=@()
    $Today = Get-Date -Format "dd-MMM"
    $Folder= "C:\Estinto\$Today\Temporary"
    $Search= "C:\Test"
    #se gli elementi da ricercare dovessero aumentare si potrebbe creare un array che li contenga
    
    if(!(Test-Path $Folder)){
        New-Item -Path $Folder -ItemType directory
    }
    
    $Ent=Get-ChildItem $Search -Recurse -Force -include *.$$$, *.$YY$, *.tmp, *.temp, *.DS_Store, *.part, ~$*, Thumbs.db, *.wbk
    foreach($i in $Ent){
        if($i.mode -match "h"){
            $split=$i.Attributes -split(', ')
            $join=($split | where{$_ -ne "Hidden"}) -join(', ')
            if($join -eq ""){
                $i.Attributes = [System.IO.FileAttributes]::Normal
            }
            else{
                $i.Attributes = $join
            }
        }
        $Move=$Move+$i.fullname
        $Divide=$i.directory.fullname -split('')
        $Impera=""
        foreach($count in $divide){
            if($count -match ':'){
                $count="$" 
            }
            $Impera += $count
        }
        $Dest=$Folder,$impera -join('\')
            if(!(Test-Path $Dest)){
                New-Item -Path $Dest -ItemType directory
            }
            
        Move-Item $i.fullname -Destination $Dest 
    }
    
    
    $Move= $Move | sort{Split-Path( $_ ) -leaf}
    
    $drives = foreach ($path in $Move) {
                $prophash = @{
                Nome= split-path $path -leaf
                Percorso = $path
                }
    
            #create a new object from the property hash
            New-Object PSObject -Property $prophash
            }

    Grazie di tutto!!

    Mi metto all'opera!

    A

    P.S.: Non c'è scritto ma tutti quei $drives, come accennato sopra, servono per creare la tabella in html; quindi tutti i 5 script generano tabelle in html così ad ogni esecuzione so cosa hanno fatto...(task schedulato)


    • Modificato Albiz martedì 3 dicembre 2013 09:36 P.S.
    martedì 3 dicembre 2013 09:07
  • Mi fa piacere vedere che cìè qualcuno che si cimenti con lo scripting una raritatà da queste parti!

    Se ti posso dare un consiglio concentrati sulle pipeline e gli oggetti.
    Analizza fino in fondo l'idea di powershell che i cmdlet ritornano oggetti e che sono capaci di attraversare le pipe senza modificare le proprietà e i metodi che gli appartengono, questa forse è la caratteristica che differenzia di più powershell dagli altri linguaggi...

    Poi ci sono cose che powershell non riesce a far bene vedi robocopy è inegualiabile  e grazie a lui il tuo quinto script si riduce a poche righe (il log è un txt e non un html, ma quello che conta è sapere cosa è stato fatto!)

    $Move=@()
    $Today = Get-Date -Format "dd-MMM"
    $Folder= "D:\temp\$Today\Temporary"
    $Search= "C:\temp"
    #se gli elementi da ricercare dovessero aumentare si potrebbe creare un array che li contenga
    
    if(!(Test-Path $Folder)){
        New-Item -Path $Folder -ItemType directory
    }
    
    #move-item non funziona fa dischi diversi !!!!!!! meglio robocopy
    $cmd=robocopy $Search $Folder  *.$$$ *.$YY$ *.tmp *.temp *.DS_Store *.part ~$* Thumbs.db *.wbk  /s /move /r:1 /w:0
    $cmd >c:\temp\mv.log

    ciao Gas


    Gastone Canali >http://www.armadillo.it

    Se alcuni post rispondono al tuo quesito(non necessariamente i miei), ricorda di contrassegnarli come risposta e non dimenticare di contrassegnare anche i post utili. GRAZIE! Ricorda di dare un occhio ai link Click Here andHere

    martedì 3 dicembre 2013 11:11
  • Rimuovo il post nel forum usa e lascio qui lo script per spostare le dir vuote

    function move-emptydir ($source,$destination,$no_match){
    dir $source | 
     Where-Object {$_.PSISContainer} |
      ForEach-Object {move-emptydir -source $_.fullname -destination $destination -no_match $no_match }
      if (($_ -ne $null) -and  ((Get-ChildItem -Path $_.FullName) -eq $null)-and  (($_.Name)[0]  -notmatch "$RegExp")) {
    	 $Move=$_.fullname -replace ":","$"
    	 $Dest="$Folder\$Move"
             New-Item -Path $Dest -ItemType directory -Force  
    	 remove-Item -Path $_.FullName -force
      }	
    }
    $RegExp="0|9|5|a"
    $Today = Get-Date -Format "dd-MMM"
    $Folder= "C:\Estinto\$Today\EmptyFolders"
    $Search= "C:\Temp"
    cls
    if(!(Test-Path $Folder)){New-Item -Path $Folder -ItemType directory}
    move-emptydir -source $Search -destination $Folder -no_match $RegExp 
    "Fine--------------------------------------------"

    ciao


    Gastone Canali >http://www.armadillo.it

    Se alcuni post rispondono al tuo quesito(non necessariamente i miei), ricorda di contrassegnarli come risposta e non dimenticare di contrassegnare anche i post utili. GRAZIE! Ricorda di dare un occhio ai link Click Here andHere

    martedì 3 dicembre 2013 21:00
  • Poi ci sono cose che powershell non riesce a far bene vedi robocopy è inegualiabile  e grazie a lui il tuo quinto script si riduce a poche righe (il log è un txt e non un html, ma quello che conta è sapere cosa è stato fatto!)

    ...

    #move-item non funziona fa dischi diversi !!!!!!! meglio robocopy $cmd=robocopy $Search $Folder *.$$$ *.$YY$ *.tmp *.temp *.DS_Store *.part ~$* Thumbs.db *.wbk /s /move /r:1 /w:0 $cmd >c:\temp\mv.log

    Peccato che quando chiedo get-help robocopy ottengo:

    Get-Help : Impossibile trovare la Guida per l'argomento "robocopy".
    In riga:1 car:9
    + Get-Help <<<<  robocopy
        + CategoryInfo          : ResourceUnavailable: (:) [Get-Help], HelpNotFoun
       dException
        + FullyQualifiedErrorId : HelpNotFound,Microsoft.PowerShell.Commands.GetHe
       lpCommand

    Powershell v2

    Eppure dovrebbe funzionare...

    Già che ci sono allego anche questo, i primi incerti passi XD:

    $global:Move=@()
    $Today = Get-Date -Format "dd-MMM"
    $Folder= "C:\Estinto\$Today\Temporary"
    $Search= "C:\Test"
    
    
    if(!(Test-Path $Folder)){
        New-Item -Path $Folder -ItemType directory
    }
    
    function Hide_And_Temp{
        Dir $Search -Recurse -Force -Include *.$$$, *.$YY$, *.tmp, *.temp, *.DS_Store, *.part, ~$*, Thumbs.db, *.wbk |
        ForEach-Object{
            if($_.mode -match "h"){
                $_.mode -replace "h","-"
            }
            $global:Move+=$_.fullname
            $From=$_.Directory -replace ":","$"
        }
        return $From
    }
    
    $impera=Hide_And_Temp
    $Dest=$Folder,$impera -join('\')
        if(!(Test-Path $Dest)){
            New-Item -Path $Dest -ItemType directory
         }
            
    Robocopy $Move $Dest
    
    $Move= $Move | sort{Split-Path( $_ ) -leaf}
    
    $drives = foreach ($path in $Move) {
                $prophash = @{
                Nome= split-path $path -leaf
                Percorso = $path
                }
    
            New-Object PSObject -Property $prophash
            }
    
    $head = @'
    <Title>Deleting Temp Report</Title>
    <style>
    body 
    { 
     background-color:#FFFFFF;
     font-family:Verdana;
     font-size:12pt; 
    }
    td, th 
    { 
     border:1px solid blue; 
     border-collapse:collapse; 
    }
    th 
    {
     color:white;
     background-color:green; 
    }
    table, tr, td, th { padding: 5px; margin: 0px }
    table { margin-left:50px; }
    </style>
    '@
    
    [xml]$html = $drives | ConvertTo-Html -fragment
    
    $body = @'
    <H1>Deleting Temp Report for $Search</H1>
    $($html.innerxml)
    '@
    
    $Log = "Deleting Temp " + $Today
    
    ConvertTo-HTML -head $head  -PostContent "<br><i>$(Get-date)</i>  Files will be moved to $Folder" -body $body | 
    Out-File "C:\Log\$Log.html" -Encoding ascii

    L'html continuo a preferirlo a modo mio, impiega 40 righe ma al momento lo tengo così...

    La funzione è "esterna al main" , non gli vengono passati valori e utilizza il return; magari non è gran che, anche perché quel $_.mode -replace non funziona a dovere penso.

    • Modificato Albiz giovedì 5 dicembre 2013 14:20 Script
    giovedì 5 dicembre 2013 10:29
  • Dimenticavo robocopy.exe non è un cmdlet powershell ...

    Per l'help Robocopy /?

    http://technet.microsoft.com/en-us/library/cc733145.aspx

    http://ss64.com/nt/robocopy.html


    Gastone Canali >http://www.armadillo.it

    Se alcuni post rispondono al tuo quesito(non necessariamente i miei), ricorda di contrassegnarli come risposta e non dimenticare di contrassegnare anche i post utili. GRAZIE! Ricorda di dare un occhio ai link Click Here andHere

    • Contrassegnato come risposta Albiz mercoledì 11 dicembre 2013 09:40
    lunedì 9 dicembre 2013 09:18