Redgate Hub

Mientras trabajas con una sesión de PowerShell, puedes modificar su estado (o entorno) de cualquier manera: Puedes importar módulos, crear funciones, especificar alias o definir variables, por nombrar algunas. Pero todas ellas son transitorias: sus efectos desaparecen en cuanto se cierra la sesión de PowerShell. Sin embargo, PowerShell proporciona un mecanismo, llamado perfil PowerShell, que le permite recrear cualquiera de estas construcciones y configuraciones de entorno cada vez que inicie una nueva sesión de PowerShell. Y si un perfil es bueno, ¿no sería mejor tener más? Resulta que cualquier sesión de PowerShell puede utilizar uno (o todos) de los cuatro perfiles diferentes, y los diferentes hosts de PowerShell proporcionan aún más opciones de perfiles: Pero en realidad es mucho más sencillo de usar de lo que podría parecer, una vez que se aprecia dónde encajan todas las piezas móviles. Echemos un vistazo.

El Shell en PowerShell

Hay una distinción entre el shell y el host en PowerShell. Don Jones, en su post acertadamente titulado The Shell vs. The Host, explica que usted-como usuario escribiendo en el teclado-no interactúa directamente con el shell (o motor) de PowerShell. Más bien, interactúas con una aplicación host (como powershell.exe o powershell_ise.exe) que crea un espacio de ejecución (una instancia del motor de PowerShell). Puedes ver la distinción mostrando el contenido de la propiedad Name de la variable de sistema $Host junto a la variable $ShellId. Verá que, si examina los valores de los tres hosts de PowerShell más comunes, todos utilizan el mismo motor (shell) mientras que cada uno presenta una interfaz de usuario diferente (host).

Host $Host.Name $ShellId
PowerShell ConsoleHost Microsoft.PowerShell
PowerShell ISE Windows PowerShell ISE Host Microsoft.PowerShell
Consola del gestor de paquetes de Visual Studio Host del gestor de paquetes Microsoft.PowerShell

Otros hosts de PowerShell podrían tener potencialmente valores $ShellId diferentes (por ejemplo, algunos de los IDEs de PowerShell disponibles gratuitamente incluyen PowerGUI, PowerShell Analyzer y PowerShell Plus, pero no he comprobado sus valores $ShellId).

El perfil de PowerShell

Un perfil de PowerShell no es más que un nombre elegante para un script que se ejecuta cuando se inicia un host de PowerShell. Citando la ayuda estándar de PowerShell en about_profiles, «Puedes usar el perfil como un script de inicio de sesión para personalizar el entorno. Puede añadir comandos, alias, funciones, variables, snap-ins, módulos y unidades de Windows PowerShell. También puede agregar otros elementos específicos de la sesión a su perfil para que estén disponibles en cada sesión sin tener que importarlos o volver a crearlos».

Cada host de PowerShell admite en realidad dos perfiles, uno es a nivel de usuario, distinto para cada usuario, y el otro es a nivel de sistema, común a todos los usuarios. Esto debería ser un paradigma familiar que habrás visto con muchas aplicaciones de Windows. Sin embargo, PowerShell añade su propio giro: distingue de forma similar entre el nivel de host (un perfil para cada host) y el nivel de sistema (un perfil común para todos los hosts).

Así, tomando todas las combinaciones de usuarios y hosts, usted podría potencialmente utilizar cualquiera (o todos) de cuatro perfiles diferentes:

  • TodosLosUsuariosTodosLosHosts
  • TodosLosUsuariosElHostActual
  • ElUsuarioActualTodosLosHosts
  • ElUsuarioActualElHostActual

Todos estos perfiles coexisten pacíficamente, por lo que debe tener en cuenta la precedencia: se enumeran arriba en el orden de ejecución. Si definieras la misma variable en los cuatro perfiles, la variable, una vez que lances un host de PowerShell y finalmente recibas un prompt, tendrá el valor asignado por el último perfil, CurrentUserCurrentHost, porque cada perfil procesado sucesivamente sobrescribirá esa variable con su valor. Otro ejemplo, que muestra la cooperación entre perfiles en lugar de la contención, podría ser incrementar una variable. Primero se define y se inicializa a un valor inicial (por ejemplo, $someVar = 0) en AllUsersAllHosts, luego se incrementa en cada uno de los otros perfiles (por ejemplo, $someVar++ o tal vez $someVar += 5 dependiendo de lo que se quiera hacer con ella).

En cuanto a cuál de los cuatro utilizar, eso depende en gran medida de sus propias necesidades: si usted utiliza un ordenador dedicado (es decir, no compartido con nadie más) no necesita preocuparse por los perfiles «todos los usuarios». Si usas varios hosts, puede que quieras diferenciar algunas cosas entre un perfil de «todos los hosts» y un perfil de host específico. Daré más detalles sobre esto más adelante en «¿Cuántos perfiles necesitas?»

La variable $Profile

Para crear o editar cualquiera de los perfiles, por supuesto, necesitas saber dónde encontrarlos. El propio PowerShell puede decírtelo fácilmente, pero también puede simplemente abrir uno para editarlo sin que tengas que molestarte explícitamente con la ruta. Para ver la ruta, muestra el valor de la variable $Profile. Revela una única ruta de archivo, que es la ruta del perfil CurrentUserCurrentHost. En un host estándar de PowerShell, el mío muestra esto:

1
2

PS> $Perfil
C:\Nsers\msorens\Documents\NWindowsPowerShell\NMicrosoft.PowerShell_profile.ps1

Nota que esto no hace ninguna afirmación sobre si el archivo existe, sólo que esa es la ruta que debe tener si existe. Para comprobar la existencia, utilice Test-Path:

1
2

PS> Test-Path $Profile
True

Si el perfil no existe, puedes crearlo fácilmente:

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

Y si quieres tenerlo en un script, puedes entonces combinar lo anterior, creando el archivo sólo si es necesario:¡

1
2

PS> if (!(Test-Ruta $Perfil)) {
Nuevo elemento -Tipo archivo -Ruta $Perfil -Forzar }

Por último, para editar el perfil, sólo tienes que invocar tu editor favorito en el archivo, o siempre puedes utilizar el omnipresente editor de notas:

1
PS> notepad $Perfil

Todos los ejemplos anteriores son para el perfil «por defecto» (CurrentUserCurrentHost), como se ha mencionado. Pero puede aplicar todos los mismos comandos a cualquiera de los cuatro perfiles por referencia específica; cualquiera de ellos produce el mismo resultado:

1
2

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

Sustituye cualquiera de los otros tres nombres de propiedades de perfil para acceder a uno de los perfiles no predeterminados.

Ten en cuenta que puede ser complicado crear o guardar archivos en los directorios del sistema (donde se almacenan los perfiles de «todos los usuarios»).

Se puede hacer con ciertas herramientas de Microsoft (por ejemplo, el bloc de notas) pero no se puede con ciertas herramientas que no son de Microsoft (por ejemplo, mi editor favorito, vim). Curiosamente, vim pretendía funcionar para mí pero en realidad no lo hacía: Podía crear un archivo, cerrar el editor completamente, luego reabrir el editor y recuperar el archivo, pero el archivo no aparecía en el Explorador de Windows ni era visto por PowerShell al iniciarse. (No sé muy bien la causa raíz de este problema, pero no se debe a la falta de privilegios elevados).

Por otro lado, el bloc de notas aparentemente conoce el encantamiento secreto, ya que funciona como se esperaba. Otra solución es crear su perfil de «todos los usuarios» en su propio directorio de usuario y luego simplemente copiarlo o moverlo al directorio apropiado del sistema.

Nombres de archivos de perfil & Ubicaciones

La sección anterior explicó cómo editar cualquiera de sus perfiles sin saber realmente dónde están en el sistema de archivos, pero usted es un desarrollador; ¡tiene una compulsión innata por saber dónde se esconden! La tabla muestra la ruta de cada uno. (HostId es un marcador de posición, se explica en un momento).

Perfil Localización
Todos los usuariosTodos los hosts $PsHome\_perfil.ps1
AllUsersCurrentHost $PsHome\HostId_profile.ps1
CurrentUserAllHosts $Home\Documents\WindowsPowerShell\profile.ps1
CurrentUserCurrentHost $Home\DocumentsWindowsPowerShell\HostId_profile.ps1

Un error que he visto en varios artículos disponibles en la web es que los perfiles de «todos los usuarios» están bajo $env:WinDir\System32. Esto es incorrecto. $PsHome puede coincidir con $env:WinDir\System32 para algunos hosts pero no para todos. Como ejemplo, en mi sistema la consola del gestor de paquetes de Visual Studio almacena sus perfiles de «todos los usuarios» bajo $env:WinDir\SysWOW64. (Este error aparece incluso en artículos de fuentes muy reputadas, como este artículo de MSDN.)

Revisando las ubicaciones, es sencillo entender las convenciones de nomenclatura. Los perfiles a nivel de sistema -los de todos los usuarios- están en el directorio del sistema apuntado por $PsHome. Los perfiles a nivel de usuario están en el directorio principal de cada usuario. El único punto que necesita explicación es el HostId que se muestra para los perfiles específicos del host en la tabla. Desgraciadamente, este identificador de host no tiene ningún parecido directo con la descripción del host ni con la propiedad $Host.Name. La forma de descubrir el HostId es simplemente mostrar el valor de la variable $Profile, ya que forma parte de la ruta. Por conveniencia, aquí están los valores de HostId para los hosts más comunes:

Host HostId $Host.Name
PowerShell Microsoft.PowerShell ConsoleHost
PowerShell ISE Microsoft.PowerShellISE Host de Windows PowerShell ISE
Consola del Gestor de Paquetes de Visual Studio NuGet Host del Gestor de Paquetes

Otro error por ahí en la naturaleza, aunque menos común, es que HostId es equivalente a la variable $ShellId mencionada anteriormente. Esto es incorrecto. Como has visto, los tres hosts comunes que se muestran arriba tienen el mismo $ShellId, y que casualmente coincide con el HostId sólo para el host estándar de PowerShell. (Este error aparece, por ejemplo, en el libro Windows PowerShell Unleashed.)

¿Cuántos perfiles necesitas?

Cualquier sistema estándar de Windows tiene dos perfiles para el host estándar de PowerShell, dos para el host de PowerShell ISE, y dos para todos los hosts-seis en total como mínimo. Añadir en el gestor de paquetes VS que he mostrado, eso es dos más. Añade otros IDEs de PowerShell-dos más para cada uno. Cómo se gestionan tantos perfiles?

Interpretando la doctrina oficial de MSDN ( about_profiles) considera una solución tripartita.

  • Primero, poner las cosas verdaderamente comunes en AllUsersAllHost.
  • Segundo, si hay algunas peculiaridades en hosts particulares, usar AllUsersCurrentHost para esos hosts malintencionados.
  • Por último, deje que cada usuario gestione sus propias preferencias y configuraciones en perfiles específicos de usuario.

Pero de nuevo, podría haber muchos perfiles diferentes para el «host actual», tanto a nivel de sistema como a nivel de usuario. Una buena referencia, como usted está reflexionando sobre sus opciones aquí, es Ed Wilson (The Scripting Guy) post Decidir entre uno o varios perfiles de PowerShell. Allí incluye una lista de ventajas y desventajas para elegir entre un perfil y varios perfiles. Pero para mí, las ventajas de un solo perfil superan con creces las ventajas de múltiples perfiles. Se necesita un poco más de trabajo para configurar, pero todavía se puede tener en cuenta las diferencias específicas de acogida, mientras que tener todo en un solo lugar, por lo que es mucho más sencillo de mantener en el tiempo.

La mayoría de las cosas de su perfil probablemente será común a cualquier host, por lo que el uso de un solo perfil significa que cualquier cambio en el camino se hará exactamente en un archivo. Para las cosas que difieren entre los hosts, sólo incluye una sección específica para cada host que te importa. El mío incluye algo como esto:

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
# ¡diferenciar verbose de warnings!
$privData = (Get-Host).PrivateData
$privData.VerboseForegroundColor = «cyan»
}
elseif ($Host.¡Name -like ‘*ISE Host’)
{
Start-Steroids
Import-Module PsIseProjectExplorer
}
if (!$env:github_shell)
{
# no sé por qué, pero esto falla en un host con sabor a git
Add-PSSnapin Microsoft.TeamFoundation.PowerShell
}

Nota cómo identifico el host actual con $Host.Name y luego ejecuto selectivamente el código. Usted ve los ejemplos anteriores para el host estándar de PowerShell, así como el host de PowerShell ISE. Para el primero, carga la extensión PSReadline que sólo funciona en el host de PowerShell. Para el segundo, carga el PsISEProjectExplorer y el módulo ISE Steroids, ambos sólo pueden funcionar en el entorno ISE.

Por último, si utiliza Git y el instalador particular que utilizó creó un host Git PowerShell para usted, tenga en cuenta que no se puede distinguir del host PowerShell estándar por el $Host.Name. En su lugar, identifiqué una variable definida de forma única, $env:github_shell, que sólo está presente en el host con sabor a Git.

Qué poner en tu perfil

La respuesta a esta pregunta depende de tus necesidades individuales, por lo que tiene que ser muy abstracta: pon en tu perfil lo que puedas utilizar para ser más productivo. Por lo tanto, no hay una respuesta única a esto, pero sí que puedo dar algunas sugerencias para que fluya tu creatividad. Acabas de ver arriba algunos ejemplos de importación de módulos de terceros para añadir funcionalidad al host dado. Además de las importaciones de módulos, las siguientes secciones proporcionan sólo algunas ideas para que pienses en lo que podrías querer poner allí. También, eche un vistazo a ¿Qué hay en su archivo PowerShell `profile.ps1`? (en StackOverflow) para ver muchos más ejemplos.

Alias

Muchos cmdlets de PowerShell incorporados tienen alias; algunos tienen múltiples alias. Usted puede, por ejemplo, utilizar ls o dir o gci en lugar de escribir Get-ChildItem. Para aquellos que utiliza regularmente y que no proporcionan alias (o para sus propias funciones y cmdlets personalizados) puede crear sus propios alias con Set-Alias. Set-Alias es apropiado si sólo quiere abreviar el nombre del cmdlet o función. Pero a veces puede querer que un alias incluya, por ejemplo, el nombre de un cmdlet más un parámetro que suele utilizar siempre. En este caso, puede emular un alias con una función simple. Para ilustrarlo, considere el cmdlet Import-Module. Lo uso con frecuencia y prefiero que todos mis cmdlets de uso frecuente utilicen sólo la primera letra de cada componente. Esto establece el alias im para hacer eso:

1
Set-Alias im Import-Module

Pero al ser un desarrollador, también necesito usar frecuentemente Import-Module con el interruptor -Force. Así que para eso, necesito recurrir a una función. Para mi convención de nomenclatura, añado la primera letra del switch, de ahí imf aquí:

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

Puedo entonces usar, por ejemplo, im foobar para hacer una importación vainilla, o imf foobar para importar con -Force aplicada.

Funciones simples

Se acaban de mencionar las funciones como un medio para crear pseudoalias, es decir, esencialmente para ahorrarte escribir. Pero, por supuesto, no se limitan a ese propósito. Es posible que quieras incluir en tu perfil una variedad de «frases únicas» que te ahorren teclear y que te ahorren tener que recordar los detalles de los cmdlets que utilizas con menos frecuencia. Rápido, ¿cómo mostrar los últimos 50 elementos de tu historial de comandos? ¿No está seguro? El cmdlet a utilizar es Get-History (tiene un alias estándar de sólo la letra h). ¿Es fácil recordar Get-History -Count 50 o simplemente h50? Aquí está mi definición para h50 (y un h10 lanzado en sólo para una buena medida):

1
2

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

Aquí hay una función más interesante. ¿Cómo revelarías el cmdlet detrás de un alias, la ruta de un ejecutable dado sólo el nombre del programa, los conjuntos de parámetros disponibles para un cmdlet dado, o el contenido de una función definida por el usuario? Yo uso este one-liner para hacer todo eso (llamado así por el comando de unix/linux que realiza algo similar):

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

Aquí tienes algunos resultados de su uso:

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

PS> que h
Get-Historial
PS> que notepad
C:\WindowsSystem32notepad.exe
PS> que
param($cmd)
(Get-Command $cmd).Definición
PS> which import-module
Import-Module <string> <string> -PSSession <PSSession>
Import-Module <string> -CimSession <CimSession>

Finalmente, otro práctico one-liner que, de nuevo, funciona como el comando de unix/linux, informando de su nombre de usuario calificado por el dominio:

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

Funciones complejas

Es probable que en el transcurso de su trabajo cree algunas funciones de utilidad que sean mucho más complicadas que un simple one-liner. Usted podría, por supuesto, simplemente incrustarlos en su perfil, pero eso tiende a hacer su perfil largo y desordenado. Si tienes una colección de tales funciones, siempre puedes organizarlas en un verdadero módulo de PowerShell y luego simplemente añadir un Import-Module en tu perfil para traerlas. Para algo intermedio entre esos dos extremos, considere este enfoque. En mi perfil tengo esta secuencia de comandos que trae todas las secuencias de comandos en mi directorio local profile_scripts en el ámbito de aplicación en el inicio:

1
2
3

Resolve-Path $PSScriptRoot\\\_profile_scripts\*.ps1 |
Where-Object { -not ($_.ProviderPath.Contains(«.Tests.»)) }
Foreach-Object { . $_.ProviderPath }

$PSScriptRoot es una variable estándar del sistema que sólo existe dentro del contexto de un script en ejecución. Se resuelve a $Home\NDocumentos\NWindowsPowerShell\N. Por lo tanto, mi directorio profile_scripts está bajo esa ruta. Cualquier script (sin los scripts de prueba) es dot-sourced, haciéndolo visible en su ámbito actual tan pronto como tenga un prompt después del arranque.

Realizar Acciones

Los ítems anteriores en esta sección son todos pasivos; definen cosas para que usted use en algún momento futuro después del arranque. Pero también puede incluir elementos activos que se ejecuten durante el arranque. Te recomiendo que utilices el cmdlet «no dejes que me dispare en el pie»:

1
Set-PSDebug -Strict

Los lenguajes que son de tipado débil (e.ej. JavaScript, PowerShell) te dan la opción de trabajar de forma segura o no, mientras que los lenguajes fuertemente tipados generalmente te obligan a trabajar de forma segura. Principalmente, seguridad significa no permitir el uso de una variable antes de ser declarada. Esto evita que un error de escritura inadvertido le cause un dolor incalculable al tratar de averiguar por qué su código no funciona. (Es de suponer que la gente de los lenguajes de tipado débil piensa que algunas personas podrían encontrar pesado el operar con seguridad, así que lo hacen una opción). Simplemente pon el Set-PSDebug en tu perfil. Esté seguro. Por favor.

Otros tipos de acciones que podrías poner en tu perfil son cosas como mostrar algunas estadísticas, por ejemplo, tiempo de actividad, espacio en disco o política de ejecución de PowerShell. Si usted administra varias máquinas es posible que desee ver los detalles acerca de la máquina que ha ‘remoto’ en, para asegurarse de que está en la caja que usted piensa que es (nombre de dominio, nombre del equipo, dirección IP, etc.).

Securing Your Profile

Cuando se trata de computadoras, la seguridad debe ser siempre una consideración. Usted utiliza un firewall y un antivirus para tratar de mantener su sistema seguro. Del mismo modo, debe considerar los scripts de PowerShell que ejecuta – incluyendo sus propios perfiles. PowerShell tiene un buen soporte para la seguridad, comenzando con su configuración por defecto de no permitirte ejecutar ningún script; fuera de la caja sólo puedes usar comandos de PowerShell de forma interactiva. Necesita abrir su sistema lo suficiente como para permitirle realizar cualquier trabajo que necesite hacer configurando la política de ejecución de su equipo (ver Set-ExecutionPolicy).

Pero tan pronto como permita ejecutar scripts, existe la posibilidad de que inadvertidamente esté ejecutando un script comprometido. Esto no es nada exclusivo de los scripts de PowerShell en sí -cualquier cosa en su computadora podría estar comprometida- es sólo que PowerShell le ayuda a mitigar la situación. Y lo hace permitiéndole establecer la política de ejecución a diferentes niveles de seguridad según sus propias necesidades. Puedes exigir que todos los scripts se autentifiquen, o que sólo se autentifiquen los scripts que descargues, entre otras opciones. La autenticación, en este caso, se refiere a la firma de los scripts con una firma digital (ver Set-AuthenticodeSignature) de modo que, si un archivo es modificado (maliciosamente o no), la firma digital detectaría la manipulación y evitaría que el script se ejecute.

La gestión de la seguridad de sus scripts de PowerShell (incluyendo sus perfiles), sin embargo, no es un esfuerzo trivial. (¡Sería más del doble de la longitud de este artículo!) Pero una gran cantidad de buena información ya está por ahí para guiarte. Yo recomendaría empezar con otro artículo aquí en Simple-Talk, Nicolas Prigent’s PowerShell Day-to-Day SysAdmin Tasks: Securing Scripts. También hay varias buenas referencias en la propia documentación de PowerShell: about_signing da una buena introducción al tema; New-SelfSignedCertificate le permite crear sus propios certificados autofirmados, y Get-ChildItem for Certificate revela las diferencias poco conocidas en Get-ChildItem al hacer referencia a su almacén de certificados. Microsoft ofrece una referencia antigua pero aún útil sobre las mejores prácticas de firma de código. Y también merece la pena echar un vistazo a Signing PowerShell Scripts de Geoff Bard.

¡Saque ese perfil del camino!

Ahora ya sabe cómo configurar su perfil, por qué es útil, qué hacer con él y cómo salvaguardarlo. Pero como cualquier superpoder, tienes que ser consciente de su lado oscuro. Bueno, no tanto de un lado oscuro en sí, sino de que hay veces que simplemente no quieres que tu(s) perfil(es) se interponga(n) en tu camino. O, más conmovedoramente, los perfiles de otras personas.

Hay una variedad de situaciones en las que es posible que desee ejecutar realmente powershell.exe ya sea con un comando literal o un archivo de secuencia de comandos para ejecutar. Aquí es sólo un ejemplo: digamos que usted ha creado un script de PowerShell que desea compartir con un colega. A diferencia de los archivos por lotes, no puedes simplemente hacer doble clic en un script de PowerShell para ejecutarlo; eso es parte del modus operandi de seguridad de PowerShell para mantener tu sistema seguro. Pero eso es fácil de eludir (¡no es que lo recomiende!) creando un acceso directo estándar de Windows dirigido a powershell.exe con su archivo de script de PowerShell como parámetro.

Otro uso, quizás más legítimo, sería ejecutar un script o comando de PowerShell dentro de un archivo de construcción. Dado que MSBuild no sabe de forma innata cómo ejecutar scripts de PowerShell, normalmente se ejecutaría un script proporcionándolo como argumento a powershell.exe.

Sin embargo, cada vez que se ejecuta powershell.exe, se abre un nuevo host de PowerShell. ¿Y qué sucede cuando se abre un host? Se ejecuta cualquiera (o todos) de sus cuatro perfiles. Pero casi siempre que abra un host invocando directamente powershell.exe no querrá que se ejecuten sus perfiles, ni por la sobrecarga ni por los posibles conflictos que puedan surgir. Tenga en cuenta que si otra persona está ejecutando una compilación en la que usted ha introducido un comando para ejecutar powershell.exe, es su perfil el que se ejecutará en su máquina, y usted no tiene idea de lo que podría estar al acecho. Además, no quieres depender de algo en un perfil porque la primera vez que alguien ejecute tu compilación que no conozca la dependencia, fallará (posiblemente de forma misteriosa). Así que lo más seguro es simplemente adoptar la mejor práctica de ignorar siempre los perfiles cuando invocas powershell.exe. (No quiero decir que debas ignorarlos, sino que debes decirle a PowerShell que los ignore, ¡por supuesto!

Así que después de toda esa acumulación de suspense, el desenlace puede ser un poco anticlimático: simplemente añade un -NoProfile como parámetro a powershell.exe.

Conclusión

El perfil de PowerShell es tu amigo. Con la hoja de ruta expuesta en este artículo, has visto los tipos de perfiles que tienes a tu disposición y puedes seleccionar los que te servirán. O puede optar por utilizar un único perfil, distinguiendo los elementos específicos del host según sea necesario. El perfil es una herramienta simple pero poderosa a su disposición, nada complicada de usar, y sus usos están limitados sólo por su imaginación. El único inconveniente es todo el tiempo que vas a dedicar a buscar cosas útiles e inteligentes para añadir a tu perfil. (¿He mencionado que deberías considerar incluir el módulo Go-Shell…?)

Deja una respuesta

Tu dirección de correo electrónico no será publicada.