Redgate Hub

Mentre lavori con una sessione PowerShell, puoi modificare il suo stato (o ambiente) in molti modi: Puoi importare moduli, creare funzioni, specificare alias o definire variabili, per citarne alcuni. Ma tutti questi sono transitori: i loro effetti scompaiono non appena si chiude la sessione PowerShell. Tuttavia, PowerShell fornisce un meccanismo, chiamato profilo PowerShell, che consente di ricreare tali costrutti e impostazioni ambientali ogni volta che si avvia una nuova sessione PowerShell. E se un profilo è buono, non sarebbe meglio averne di più? Si scopre che ogni singola sessione di PowerShell può utilizzare uno qualsiasi (o tutti) di quattro profili diversi, e diversi host PowerShell forniscono ancora più scelte di profili: Ma in realtà è molto più semplice da usare di quanto possa sembrare, una volta che si apprezza dove si inseriscono tutte le parti mobili. Diamo un’occhiata.

La Shell in PowerShell

C’è una distinzione tra la shell e l’host in PowerShell. Don Jones, nel suo post giustamente intitolato The Shell vs. The Host, spiega che tu – come utente che digita sulla tastiera – non interagisci direttamente con la shell (o motore) di PowerShell. Piuttosto, più precisamente, si interagisce con un’applicazione host (come powershell.exe o powershell_ise.exe) che crea un runspace (un’istanza del motore PowerShell). Puoi effettivamente vedere la distinzione visualizzando il contenuto della proprietà Name della variabile di sistema $Host accanto alla variabile $ShellId. Vedrai che, se esamini i valori per i tre host PowerShell più comuni, tutti usano lo stesso motore (shell) mentre ognuno presenta un’interfaccia utente diversa (host).

Host $Host.Name $ShellId
PowerShell ConsoleHost Microsoft.PowerShell
PowerShell ISE Windows PowerShell ISE Host Microsoft.PowerShell
Visual Studio Package Manager Console Package Manager Host Microsoft.PowerShell

Altri host PowerShell potrebbero potenzialmente avere valori $ShellId diversi (per esempio, alcuni degli IDE PowerShell disponibili gratuitamente includono PowerGUI, PowerShell Analyzer e PowerShell Plus, ma non ho controllato i loro valori $ShellId).

Il profilo PowerShell

Un profilo PowerShell non è altro che un nome di fantasia per uno script che viene eseguito quando si avvia un host PowerShell. Citando la guida standard di PowerShell su about_profiles, “Puoi usare il profilo come uno script di logon per personalizzare l’ambiente. Puoi aggiungere comandi, alias, funzioni, variabili, snap-in, moduli e unità Windows PowerShell. Puoi anche aggiungere altri elementi specifici della sessione al tuo profilo in modo che siano disponibili in ogni sessione senza doverli importare o ricreare.”

Ogni host PowerShell supporta effettivamente due profili, uno è a livello utente, distinto per ogni utente, e l’altro è a livello di sistema, comune a tutti gli utenti. Questo dovrebbe essere un paradigma familiare che avrete visto con molte applicazioni Windows. PowerShell aggiunge il suo tocco unico, però: allo stesso modo distingue tra livello host (un profilo per ogni host) e livello sistema (un profilo comune per tutti gli host).

Quindi, prendendo tutte le combinazioni di utenti e host, si potrebbe potenzialmente usare qualsiasi (o tutti) i quattro diversi profili:

  • AllUsersAllHosts
  • AllUsersCurrentHost
  • CurrentUserAllHosts
  • CurrentUserCurrentHost

Questi profili coesistono tutti pacificamente quindi devi essere consapevole della precedenza: sono elencati sopra in ordine di esecuzione. Se tu dovessi definire la stessa variabile in tutti e quattro i profili, la variabile, una volta lanciato un host PowerShell e finalmente ricevuto un prompt, avrà il valore assegnato dall’ultimo profilo, CurrentUserCurrentHost, perché ogni profilo successivamente elaborato sovrascriverà quella variabile con il suo valore. Un altro esempio, che mostra la cooperazione tra i profili piuttosto che la contesa, potrebbe essere quello di incrementare una variabile. Prima definiscila e inizializzala ad un valore iniziale (ad esempio $someVar = 0) in AllUsersAllHosts, poi incrementala in ognuno degli altri profili (ad esempio $someVar++ o forse $someVar += 5 a seconda di cosa vuoi farne).

Per quanto riguarda quale dei quattro usare, questo dipende in gran parte dalle tue esigenze: se usi un computer dedicato (cioè non condiviso con nessun altro) non hai bisogno di preoccuparti dei profili “tutti gli utenti”. Se usi più host, potresti voler differenziare alcune cose tra un profilo “tutti gli host” e un profilo host specifico. Darò più dettagli su questo sotto in “Quanti profili ti servono?”

La variabile $Profile

Per creare o modificare qualsiasi profilo, naturalmente, devi sapere dove trovarlo. PowerShell stesso può facilmente dirtelo, ma può anche semplicemente aprirne uno per la modifica senza che tu debba esplicitamente preoccuparti del percorso. Per vedere il percorso, visualizza il valore della variabile $Profile. Rivela un singolo percorso di file, che è il percorso del profilo CurrentUserCurrentHost. In un host PowerShell standard, il mio mostra questo:

1
2

PS> $Profile
C:\Users\msorens\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

Nota che questo non afferma se il file esiste, solo che quello è il percorso che deve avere se esiste. Per controllare l’esistenza, usate Test-Path:

Se il profilo non esiste, puoi crearlo facilmente:

1
2

PS> Test-Path $Profile
True

1
PS> New-Item -Type file -Path $Profile -Force

E se vuoi averlo in uno script, puoi poi combinare quanto sopra, creando il file solo se necessario:

1
2

PS> if (!(Test-Path $Profile)) {
New-Item -Type file -Path $Profile -Force }

Infine, per modificare il profilo, basta invocare il tuo editor preferito sul file, o puoi sempre usare l’onnipresente editor notepad:

1
PS> notepad $Profile

Tutti gli esempi precedenti sono per il profilo “default” (CurrentUserCurrentHost), come detto. Ma puoi applicare tutti gli stessi comandi a uno qualsiasi dei quattro profili per riferimento specifico; entrambi producono lo stesso risultato:

1
2

PS> notepad $Profile
PS> notepad $Profile.CurrentUserCurrentHost

Sostituisci uno qualsiasi degli altri tre nomi di proprietà del profilo per accedere a uno dei profili non predefiniti.

Nota che può essere difficile creare o salvare file nelle directory di sistema (dove sono memorizzati i profili “tutti gli utenti”).

Lo si può fare con certi strumenti Microsoft (per esempio il blocco note) ma non con certi strumenti non Microsoft (per esempio il mio editor preferito, vim). Curiosamente, vim ha fatto finta di funzionare per me, ma in realtà non ha funzionato: Potevo creare un file, chiudere completamente l’editor, poi riaprire l’editor e richiamare il file – ma il file non appariva in Windows Explorer né veniva visto da PowerShell all’avvio! (Non conosco bene la causa principale di questo problema, ma non è dovuto alla mancanza di privilegi elevati).

D’altra parte, notepad apparentemente conosce l’incantesimo segreto, poiché funziona come previsto. Un’altra soluzione è quella di creare il vostro profilo “tutti gli utenti” nella vostra directory utente e poi semplicemente copiarlo o spostarlo nella directory di sistema appropriata.

Nomi dei file di profilo &Posizioni

La sezione precedente ha spiegato come modificare uno qualsiasi dei vostri profili senza sapere effettivamente dove sono nel file system, ma voi siete uno sviluppatore; avete una compulsione innata a sapere dove si nascondono! La tabella mostra il percorso di ciascuno. ( HostId è un segnaposto, spiegato tra poco.)

Profile Location
AllUsersAllHosts $PsHome\profile.ps1
AllUsersCurrentHost $PsHome\HostId_profile.ps1
CurrentUserAllHosts $Home\Documents\WindowsPowerShell\profile.ps1
CurrentUserCurrentHost $Home\Documents\WindowsPowerShell\HostId_profile.ps1

Un errore che ho visto in diversi articoli disponibili sul web è che i profili “tutti gli utenti” sono sotto $env:WinDir\System32. Questo non è corretto! $PsHome può casualmente risolversi in $env:WinDir\System32 per alcuni host ma non per tutti. Per esempio, sul mio sistema il Visual Studio Package Manager Console memorizza i suoi profili “tutti gli utenti” sotto $env:WinDir\SysWOW64. (Questo errore appare anche in articoli provenienti da fonti molto rispettabili, come questo articolo di MSDN.)

Esaminando le posizioni, è semplice capire le convenzioni di denominazione. I profili a livello di sistema – quelli per tutti gli utenti – sono nella directory di sistema indicata da $PsHome. I profili a livello utente sono sotto la directory home di ogni utente. L’unico punto che ha bisogno di spiegazioni è il HostId indicato nella tabella per i profili specifici dell’host. Sfortunatamente, questo host id non ha alcuna somiglianza diretta con la descrizione dell’host né con la proprietà $Host.Name! Il modo per scoprire il HostId è semplicemente quello di visualizzare il valore della variabile $Profile, poiché fa parte del percorso. Per comodità, ecco i valori HostId per gli host più comuni:

Host HostId $Host.Name
PowerShell Microsoft.PowerShell ConsoleHost
PowerShell ISE Microsoft.PowerShellISE Windows PowerShell ISE Host
Visual Studio Package Manager Console NuGet Package Manager Host

Un altro errore in libertà, anche se meno comune, è che HostId sia equivalente alla variabile $ShellId menzionata prima. Questo non è corretto! Come avete visto, tutti e tre gli host comuni mostrati poco sopra hanno lo stesso $ShellId, e questo coincide con il HostId solo per l’host PowerShell standard. (Questo errore appare, per esempio, nel libro Windows PowerShell Unleashed.)

Quanti profili ti servono?

Ogni sistema Windows standard ha due profili per l’host PowerShell standard, due per l’host PowerShell ISE, e due per tutti gli host – sei in tutto come minimo. Aggiungete il gestore di pacchetti VS che ho mostrato, sono altri due. Aggiungete altri IDE PowerShell – altri due per ciascuno. Come fate a gestire così tanti profili?

Interpretando la dottrina ufficiale di MSDN ( about_profiles) considerare una soluzione tripartita.

  • Primo, metti le cose veramente comuni in AllUsersAllHost.
  • Secondo, se ci sono alcune particolarità in particolari host, usate AllUsersCurrentHost per questi host malvagi.
  • Infine, lasciate che ogni utente gestisca le proprie preferenze e impostazioni in profili specifici.

Ma di nuovo, ci potrebbero essere molti profili diversi per “host corrente”, sia a livello di sistema che a livello di utente. Un buon riferimento, mentre stai rimuginando sulle tue scelte qui, è il post di Ed Wilson (The Scripting Guy) Decidere tra uno o più profili PowerShell. Include una lista di vantaggi e svantaggi per scegliere tra un profilo e più profili. Ma per me, i vantaggi di un singolo profilo superano di gran lunga i vantaggi di profili multipli. Ci vuole un po’ più di lavoro per impostare, ma si può ancora tenere conto delle differenze specifiche dell’host pur avendo tutto in un unico posto, rendendolo molto più semplice da mantenere nel tempo.

La maggior parte delle cose del tuo profilo saranno probabilmente comuni a qualsiasi host, quindi usare un singolo profilo significa che qualsiasi cambiamento lungo la strada sarà fatto esattamente in un file. Per le cose che differiscono tra gli host, basta includere una sezione specifica per ogni host che vi interessa. Il mio include qualcosa come questo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

if ($Host.Name -eq ‘ConsoleHost’)
{
Import-Module PSReadline
# differenzia il verbose dagli warnings!
$privData = (Get-Host).PrivateData
$privData.VerboseForegroundColor = “cyan”
}
elseif ($Host.Name -like ‘*ISE Host’)
{
Start-Steroidi
Import-Module PsIseProjectExplorer
}
if (!$env:github_shell)
{
# non so perché, ma questo fallisce in un host git-flavored
Add-PSSnapin Microsoft.TeamFoundation.PowerShell
}

Nota come identifico l’host corrente con $Host.Name poi eseguo selettivamente il codice. Vedete gli esempi qui sopra per l’host PowerShell standard e per l’host PowerShell ISE. Per il primo, carica l’estensione PSReadline che funziona solo nell’host PowerShell. Per il secondo, carica PsISEProjectExplorer e il modulo ISE Steroids, entrambi i quali possono funzionare solo nell’ambiente ISE.

Infine, se usi Git e il particolare installatore che hai usato ha creato un host Git PowerShell per te, nota che non può essere distinto dall’host PowerShell standard dal $Host.Name. Invece, ho identificato una variabile definita in modo univoco, $env:github_shell, che è presente solo nell’host Git-flavored.

Cosa mettere nel tuo profilo

La risposta a questa domanda dipende dai tuoi bisogni individuali, quindi deve essere molto astratta: metti nel tuo profilo ciò che puoi usare per essere più produttivo. Non c’è quindi una risposta unica a questo, ma posso certamente fornire alcuni suggerimenti per far scorrere i vostri succhi creativi. Avete appena visto sopra alcuni esempi di importazione di moduli di terze parti per aggiungere funzionalità all’host dato. Oltre alle importazioni di moduli, le seguenti sezioni forniscono solo alcune idee per farvi pensare a cosa potreste voler mettere lì dentro. Inoltre, dai un’occhiata a Cosa c’è nel tuo file PowerShell `profile.ps1`? (su StackOverflow) per molti altri esempi.

Alias

Molte cmdlets PowerShell integrate hanno alias; alcune hanno alias multipli. Puoi, per esempio, usare ls o dir o gci invece di digitare Get-ChildItem. Per quelli che usi regolarmente e che non forniscono alias (o per le tue funzioni e cmdlets personalizzate) puoi creare i tuoi alias con Set-Alias. Set-Alias è appropriato se vuoi solo abbreviare il nome del cmdlet o della funzione. Ma a volte potresti volere un alias che includa, per esempio, il nome di una cmdlet più un parametro che tendi a usare sempre. Per questi casi, puoi emulare un alias con una semplice funzione. Per illustrare, considerate il cmdlet Import-Module. Lo uso spesso e preferisco che tutti i miei cmdlet di uso frequente usino solo la prima lettera di ogni componente. Questo imposta l’alias im per farlo:

1
Set-Alias im Import-Module

Ma essendo uno sviluppatore, devo anche usare spesso Import-Module con lo switch -Force. Quindi, per questo, ho bisogno di ricorrere a una funzione. Per la mia convenzione di denominazione, aggiungo la prima lettera dello switch, quindi imf qui:

1
function imf($name) { Import-Module $name -force }

Posso poi usare, per esempio, im foobar per fare un’importazione vanilla, o imf foobar per importare con -Force applicato.

Funzioni semplici

Le funzioni sono state appena menzionate come un mezzo per creare pseudo-alias, cioè essenzialmente per risparmiare la digitazione. Ma, naturalmente, non si limitano a questo scopo. Potreste voler includere nel vostro profilo una varietà di “one-liners” che risparmiano sia la digitazione che il dover ricordare dettagli di cmdlets che usate meno spesso. Presto, come si fa a mostrare gli ultimi 50 elementi nella cronologia dei comandi? Non sei sicuro? La cmdlet da usare è Get-History (ha un alias standard di solo la lettera h). È facile ricordare Get-History -Count 50 o solo h50? Ecco la mia definizione di h50 (e un h10 buttato dentro per buona misura):

1
2

function h50 { Get-History -Count 50 }
funzione h10 { Get-History -Count 10 }

Ecco una funzione più interessante. Come potreste rivelare il cmdlet dietro un alias, il percorso di un eseguibile dato solo il nome del programma, i set di parametri disponibili per un dato cmdlet, o il contenuto di una funzione definita dall’utente? Io uso questo one-liner per fare tutto ciò (che prende il nome dal comando unix/linux che esegue in modo simile):

1
function which($cmd) { (Get-Command $cmd).Definition }

Ecco alcuni risultati del suo utilizzo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

PS> che h
Get-Storia
PS> quale blocco note
C:\Windows\system32\notepad.exe
PS> quale quale
param($cmd)
(Get-Command $cmd).Definizione
PS> which import-module
Import-Module <stringa> <stringa> -PSSession <PSSession>
Import-Module <stringa> -CimSession <CimSession>

Fine, un altro pratico one-liner che ancora una volta funziona come il comando unix/linux, riportando il vostro nome utente qualificato per il dominio:

1
function whoami { (get-content env:\userdomain) + “\” + (get-content env:\username }

Funzioni complesse

E’ probabile che nel corso del vostro lavoro creiate alcune funzioni di utilità che sono molto più coinvolte di un semplice one-liner. Potreste, naturalmente, semplicemente incorporarle nel vostro profilo, ma questo tende a rendere il vostro profilo lungo e disordinato. Se hai una collezione di tali funzioni potresti sempre organizzarle in un vero modulo PowerShell e poi aggiungere semplicemente un Import-Module nel tuo profilo per portarle dentro. Per qualcosa tra questi due estremi, considera questo approccio. Nel mio profilo ho questa sequenza di comandi che porta tutti gli script nella mia directory locale profile_scripts nello scope all’avvio:

1
2
3

Resolve-Path $PSScriptRoot\profile_scripts\*.ps1 |
Where-Object { -not ($_.ProviderPath.Contains(“.Tests.”)) }
Foreach-Object { . $_.ProviderPath }

$PSScriptRoot è una variabile di sistema standard che esiste solo nel contesto di uno script in esecuzione. Si risolve in $Home\Documents\WindowsPowerShell\. Quindi la mia directory profile_scripts è sotto quel percorso. Ogni script (senza alcuno script di test) è dot-sourced, rendendolo visibile nel tuo ambito corrente non appena hai un prompt dopo l’avvio.

Perform Actions

Le voci precedenti in questa sezione sono tutte passive; definiscono cose da usare in qualche momento futuro dopo l’avvio. Ma potete anche includere elementi attivi che vengono eseguiti durante l’avvio. Vi consiglio vivamente di usare il cmdlet “non fatemi sparare ai piedi”:

1
Set-PSDebug -Strict

Lingue che sono weakly-typed (es.JavaScript, PowerShell) vi danno la possibilità di lavorare in sicurezza o meno, mentre i linguaggi fortemente tipizzati generalmente vi costringono a lavorare in sicurezza. Principalmente, sicuro significa non permettervi di usare una variabile prima che sia dichiarata. Questo evita che un errore di battitura involontario vi causi un dolore incalcolabile nel cercare di capire perché il vostro codice non funziona. (Presumibilmente, quelli del linguaggio a tipizzazione debole pensano che alcune persone potrebbero trovare fastidioso operare in modo sicuro, così lo rendono un’opzione). Mettete solo il Set-PSDebug nel vostro profilo. Siate sicuri. Per favore.

Altri tipi di azioni che potreste mettere nel vostro profilo sono cose come la visualizzazione di alcune statistiche, ad esempio il tempo di attività, lo spazio su disco o la politica di esecuzione di PowerShell. Se amministri più macchine, potresti voler vedere i dettagli sulla macchina in cui hai “remotato”, per assicurarti di essere sulla macchina che pensi di essere (nome di dominio, nome del computer, indirizzo IP, ecc.).

Sicurezza del tuo profilo

Quando hai a che fare con i computer, la sicurezza deve sempre essere una considerazione. Usi un firewall e un antivirus per cercare di mantenere il tuo sistema sicuro. Allo stesso modo devi considerare gli script PowerShell che esegui, inclusi i tuoi profili. PowerShell ha un buon supporto per la sicurezza, a partire dalla sua impostazione predefinita che non ti permette di eseguire alcuno script; fuori dalla scatola puoi solo usare i comandi PowerShell in modo interattivo. Avete bisogno di aprire il vostro sistema quanto basta per permettervi di realizzare qualsiasi lavoro abbiate bisogno di fare impostando la politica di esecuzione del vostro computer (vedi Set-ExecutionPolicy).

Ma appena permettete l’esecuzione di script, c’è la possibilità che possiate inavvertitamente eseguire uno script compromesso. Questo non è niente di unico per gli script PowerShell di per sé – qualsiasi cosa sul tuo computer potrebbe essere compromessa – è solo che PowerShell ti aiuta a mitigare la situazione. E lo fa permettendovi di impostare la politica di esecuzione a diversi livelli di sicurezza secondo le vostre esigenze. È possibile richiedere che ogni script debba essere autenticato, o che solo qualsiasi script che si scarica debba essere autenticato, tra le altre opzioni. L’autenticazione, in questo caso, si riferisce alla firma degli script con una firma digitale (vedi Set-AuthenticodeSignature) in modo che, se un file viene modificato (maliziosamente o meno), la firma digitale rileverebbe la manomissione e impedirebbe l’esecuzione dello script.

Gestire la sicurezza per i tuoi script PowerShell (compresi i tuoi profili), tuttavia, non è uno sforzo banale. (Sarebbe più del doppio della lunghezza di questo articolo!) Ma ci sono già molte buone informazioni per guidarvi. Vi consiglio di iniziare con un altro articolo qui su Simple-Talk, Nicolas Prigent’s PowerShell Day-to-Day SysAdmin Tasks: Securing Scripts. Ci sono anche diversi buoni riferimenti nella documentazione di PowerShell: about_signing dà una buona introduzione all’argomento; New-SelfSignedCertificate vi permette di creare i vostri certificati autofirmati, e Get-ChildItem for Certificate rivela le differenze poco conosciute in Get-ChildItem quando si fa riferimento al vostro negozio di certificati. Microsoft fornisce un vecchio ma ancora utile riferimento sulle migliori pratiche di firma del codice. E vale la pena dare un’occhiata anche a Signing PowerShell Scripts di Geoff Bard.

Get That Profile Out the Way!

Ora sapete come impostare il vostro profilo, perché è utile, cosa fare con esso, e come salvaguardarlo. Ma come ogni super potere, devi essere consapevole del suo lato oscuro. Beh, non tanto un lato oscuro di per sé, ma che ci sono momenti in cui semplicemente non vuoi che il tuo profilo (o i tuoi profili) ti intralcino. O, in modo più toccante, i profili di altre persone.

Ci sono una varietà di situazioni in cui si potrebbe desiderare di eseguire effettivamente powershell.exe sia con un comando letterale o un file di script da eseguire. Ecco solo un esempio: diciamo che avete creato uno script PowerShell che volete condividere con un collega. A differenza dei file batch, non si può semplicemente fare doppio clic su uno script PowerShell per eseguirlo; questo fa parte del modus operandi di sicurezza di PowerShell per mantenere il sistema sicuro. Ma questo è facile da aggirare (non che lo stia raccomandando!) creando un collegamento standard di Windows che punti a powershell.exe con il tuo file di script PowerShell come parametro.

Un altro uso, forse più legittimo, sarebbe quello di eseguire uno script o comando PowerShell all’interno di un file di build. Poiché MSBuild non sa innatamente come eseguire script PowerShell, in genere si dovrebbe eseguire uno script fornendolo come argomento a powershell.exe.

Ogni volta che si esegue powershell.exe, però, si sta aprendo un nuovo host PowerShell. E cosa succede quando apri un host? Esegue uno qualsiasi (o tutti) i tuoi quattro profili! Ma quasi ogni volta che apri un host invocando direttamente powershell.exe non vuoi che i tuoi profili vengano eseguiti, né per l’overhead né per i possibili conflitti che ne potrebbero derivare. Tenete a mente che se qualcun altro sta eseguendo una build in cui avete introdotto un comando per eseguire powershell.exe, è il suo profilo che verrà eseguito sulla sua macchina, e voi non avete idea di cosa potrebbe esserci in agguato. Inoltre, non volete dipendere da qualcosa in un profilo perché la prima volta che qualcuno esegue la vostra compilazione che non conosce la dipendenza, essa fallirà (forse misteriosamente). Quindi è più sicuro adottare semplicemente la migliore pratica di ignorare sempre i profili quando invocate powershell.exe (non voglio dire che dovreste ignorarli, piuttosto che dovreste dire a PowerShell di ignorarli, naturalmente!

Quindi, dopo tutta questa suspense, l’epilogo potrebbe essere un po’ anticlimatico: basta aggiungere un -NoProfile come parametro a powershell.exe.

Conclusione

Il profilo PowerShell è tuo amico. Con la tabella di marcia esposta in questo articolo, hai visto i tipi di profili disponibili per te e puoi selezionare quelli che funzionano per te. Oppure puoi scegliere di usare un unico profilo, distinguendo ogni elemento specifico dell’host come necessario. Il profilo è uno strumento semplice ma potente a vostra disposizione, per niente complicato da usare, e i suoi usi sono limitati solo dalla vostra immaginazione. L’unico aspetto negativo è tutto il tempo che passerete a cercare cose utili e intelligenti da aggiungere al vostro profilo. (Ti ho detto che dovresti considerare l’inclusione del modulo Go-Shell…?)

.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.