ACERCA DE
Un poco más de información sobre mi
// HABILIDADES
- C# SQL Server Python ASP.NET MVC API WebService GraphQL NodeJS VB.NET VBA HTML CSS JS (ES6) AngularJS SvelteJS Unit-Test UX-UI MS-Test Automation Selenium Sonarqube Git TFS Docker Linux RabbitMQ
// LINEA DE TIEMPO
2021
Azure Fundamentals * In Progress
2020-*
2019
Microsoft C# Programming Specialist
2018-2020
2017
Microsoft Certified Solutions Associate as SQL Database Development
2016
Microsoft Certified Professional
2013
Master's degree in Information Technologies
2002-2018
Software Architect,
Software Developer,
SysAdmin
2002
Computer Science, Telematics Engineering
POST MAS RECIENTES
Desde el blog estas son las ultimas entradas, fragmentos de codigo e ideas.Siempre que ejecuto docker ps
para listar los contenedores me resulta difícil entender donde inicia y donde termina la información sobre cada contenedor:


¡Que carajos! ... el puerto ¿cúal? ... siento que casi estoy viendo la matrix
Debo admitir que después de un tiempo tus ojos se acostumbran y comienzan a hacer cosas raras pero comienzas a entender el resultado. Claro que no debería ser así, por lo que encontré que tenemos algunas opciones para mejorar el resultado.
Utilizar —format
Al utilizar la opción --format
, podemos elegir de una serie campos disponibles e incluso despegarlos de forma mas ordenada usando la sintaxis table {{.FieldName}}\t
. La opción format utiliza Go templates algo que me resulta familiar1.
table
incluye los encabezados y \t
agrega espacio entre las columnas.
Ejemplo usando --format
docker ps --format 'table {{.Names}}\t
{{.Status}} : {{.RunningFor}}\t
{{.ID}}\t
{{.Image}}'
Definitivamente resulta mucho más fácil de comprender
Sacando provecho a los Go Templates
Nos podemos poner creativos y si necesitamos por ejemplo consultar los contenedores con información de red utilicemos las características de los GoLang Templates para detectar si hay puertos y desplegarlos en cada linea:
Información de red
docker ps --format 'table {{.Names}}
\t{{.Status}}
\t{{.Networks}}
\n{{.ID}}
{{if .Ports}}
{{with $p := split .Ports ", "}}
{{range $p}}\t\t{{println .}}{{end}}
{{end}}
{{else}}
\t\t{{println "No Ports"}}
{{end}}'

Como ven mucho mejor ordenado en dos columnas y con todos los datos de la red y puertos mas legibles
Todos los containers docker ps –a
Podemos revisar todos los contenedores (activos y no activos) utilizando la opción -a
. Aprovechando los templates añadamos el tamaño en disco, como utiliza la red y la imagen de donde proviene el contenedor.
Información de todos los contenedores
docker ps -a -q --format 'table {{.Names}}\t
{{.Status}}\t
{{.Size}}\n
{{.ID}}\t
{{.Image}}
{{if .Ports}}
{{with $p := split .Ports ", "}}\t
{{len $p}} port(s) on {{end}}{{- .Networks}}
{{else}}\tNo Ports on {{ .Networks }}
{{end}}\n'
De esta manera podemos ver en general el estado de nuestros contenedores.
Crear una función para reutilizar los comandos
Finalmente podemos crear una función para utilizar estos comandos (si estas utilizando WSL o Linux) dentro de nuestro .bash_aliases
Crear una función bash que acepta parámetros
# Docker PS Prettify Function
function dock() {
if [[ "$@" == "ps" ]]; then
command docker ps --format 'table {{.Names}}\t{{.Status}} : {{.RunningFor}}\t{{.ID}}\t{{.Image}}'
elif [[ "$@" == "psa" ]]; then
# docker ps -a includes all containers
command docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Size}}\n{{.ID}}\t{{.Image}}{{if .Ports}}{{with $p := split .Ports ", "}}\t{{len $p}} port(s) on {{end}}{{- .Networks}}{{else}}\tNo Ports on {{ .Networks }}{{end}}\n'
elif [[ "$@" == "psnet" ]]; then
# docker ps with network information
command docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Networks}}\n{{.ID}}{{if .Ports}}{{with $p := split .Ports ", "}}{{range $p}}\t\t{{println .}}{{end}}{{end}}{{else}}\t\t{{println "No Ports"}}{{end}}'
else
command docker "$@"
fi
}
Para utilizarlo basta ahora con teclear:
- dock ps
- Contenedores en ejecución con su imagén
- dock psa
- Todos los contenedores incluyendo tamaño en disco y datos de red
- dock psnet
- Contenedores en ejecución con información de red
Campos disponibles en docker
- .ID
- Identificador del contenedor
- .Image
- Id de la imagén
- .Command
- Comando de ejecución
- .CreatedAt
- Tiempo cuando el contenedor se creó.
- .RunningFor
- Tiempo transcurrido desde que se inició el contenedor.
- .Ports
- Puertos utilizados por el contenedor.
- .Status
- Estado del contenedor.
- .Size
- Espacio que ocupa en disco el contenedor.
- .Names
- Nombre del contenedor.
- .Labels
- Todas las etiquetas asignadas al contenedor.
- .Label
- Valor especifico para una etiqueta. Por ejemplo ‘{{.Label “com.docker.swarm.cpu”}}’
- .Mounts
- Nombre de los volúmenes montados en este contenedor.
- .Networks
- Nombre de las redes adjuntas al contenedor.
El Problema
Un día tuve que reiniciar mi servidor linux y como Murphy no falla al regresar la instancia de docker estaba vacía sin ningún container
, después de explorar un rato y revisar que todo estuviera en orden no encontré la causa, hice lo que se debe hacer … reinicie nuevamente el servidor y ohh sorpresa ahi estaban de nuevo todos mis containers ¡Genial! pero … al momento de intentar iniciarlos

ERROR: network xxxx is ambiguous (2 matches found based on name)
Los porques
Encontré que es valido duplicar el nombre de la red en docker, mi sospecha es que al no encontrar la configuración docker generó nuevamente toda las redes porque como pueden ver se duplicaron las redes y los drivers
docker network list
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
27b6c27fdc2b bridge bridge local
33f2a0c04878 bridge bridge local
c4247d693521 host host local
9b95be563bf7 host host local
590102262bb5 none null local
25e1bf9dc86 none null local
Asi que la solucion parece simple eliminar la red duplicada y ya esta docker network rm [ID]
, lo intente pero ….

Error : bridge is a pre-defined network and cannot be removed
La solución no parece ser tan trivial, en algún foro encontré que para poder eliminar la red:
- El servicio de docker debe estar detenido
- No debe existir un contenedor usando la red
- No tener ningún contenedor creado
Por cierto para ver cuales containers están usando la red puedes usar:
Inspect network ID of a container
# Como bridge esta repetido necesitas hacerlo por Id y revisar la sección de "Containers"
docker network inspect [id || name]
"Internal": false,
{ "Network": "" },
"ConfigOnly": false,
"Containers": {},
La opción uno y dos no funcionaron y no podía darme el lujo de eliminar los containers y volverlos a crear pues ya los tenia configurados con sus volúmenes y otras cosas asi que la solución es:
Solución
- Crear una nueva red con -d especificamos el driver
docker network create -d bridge [nuevo-nombre-de-red]
-
Desconectar el o los containers de la red ambigua
docker network disconnect bridge [contenedor]
-
Conectar el o los containers hacia la nueva red
docker network connect [nuevo-nombre-de-red] [contenedor]
-
Opcional. Purgar nuestra red para eliminar aquellas redes que no estamos utilizando.
docker network rm $(docker network ls -q)
Listo ya con esto podemos iniciar nuestro container

docker start my-happy-container

El siguiente código permite entender como se construye aplicación muy simple en React
Pon atención a los comentarios en el código
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>React JS Search Results</title>
<!-- Add React and Babel References -->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
</head>
<body>
<section>
<!-- Every React app must have what’s known as an entry point.
The entry point is an HTML element where we insert our React application into the page. -->
<div id="root">
</div>
<!-- Our Script must have type="text/babel" for Babel to work-->
<script type="text/babel"> //React code will go here
// Babel converts this code that looks like HTML into valid JavaScript (JSX: JavaScript XML)
// The functions in react must be Capitalize to differentiate from normal javascript functions
// React Functions became Components
// The Component must return JSX code between parenthesis function ComponentName() { return ( ...JSX Code ... ) }
// Every React Component must return either JSX elements or other React components.
/* PROPS (Passing data to the components)
We can pass data to the function components using attributes and it is passed to the Link function as an argument
<Link attributeName="value" />
React collects and organizes the data passed to a given component as a single object.
The name for data passed to a component, such as title, is props.
All prop values exist within the function component itself on a props object.
*/
/* SEPARATE DATA FROM UI
Make and array of objects and pass down the link components through props.
Since this is a common pattern in React, it has its own method .map() that works with JSX
(don't confuse with JS array.map() it's similar but Reacts works only for JXS)
*/
const linkData = [
{
title: "React - A JavaScript Library for Building User Interfaces",
url: "https://reactjs.org",
shortUrl: "reactjs.org",
excerpt: "React makes it painless to create interactive UIs."
},
{
title: "Jorge Anaya's Blog",
url: "https://www.jorgeanaya.dev",
shortUrl: "jorgeanaya.dev",
excerpt: "Personal insights about coding and life"
},
{
title: "Google",
url: "https://www.google.com",
shortUrl: "google.com",
excerpt: "When everything fails then Google is your friend, GoogleIt!"
}
]
function Link(props) {
// We use {} curly braces to insert or interpolate dynamic values wherever we need.
return (
<div>
<a href="{props.url}">{props.title}</a>
<div>
<h3>{props.shortUrl}</h3>
</div>
<div> {props.excerpt}
</div>
</div>
)
}
// Since React components can return other React components, we can make an App Component
// The entire JSX expression is surrounded by curly braces
// JSX allows us to insert any valid JavaScript expression between curly braces.
function App() {
return (
<section>
{linkData.map(function (link) {
return (
<Link
key={link.url} // Each child in a list should have a unique "key" prop
title={link.title}
url={link.url}
shortUrl={link.shortUrl}
excerpt={link.excerpt} />
); // return end, function, map() & expression end */
})}
</section>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
</script>
</body>
</html>
Para concatenar varios videos usando ffmpeg
necesitamos tener un archivo de texto que contenga en cada linea un path a los videos:
VideoList.txt
# Se pueden utilizar rutas absolutas o relativas
file '/path/to/video1.mp4'
file '/path/to/video2.mp4'
file '/path/to/video3.mp4'
Ejecutar
# Si se utilizan rutas absolutas agremamos el parametro -safe 0
ffmpeg -f concat -safe 0 -i videolist.txt -c copy output.mp4
Para generar en automatico un archivo con la lista de videos
# Windows
(for %i in (\*.mp4) do @echo file '%i') > videolist.txt
ffmpeg -f concat -i videolist.txt -c copy output.mp4
# Bash
for f in ./\*.mp4; do echo "file '\$f'" >> videolist.txt; done
ffmpeg -f concat -i videolist.txt -c copy output.mp4
# Zsh - Todo en una linea
ffmpeg -f concat -safe 0 -i <(for f in ./\*.mp4; do echo "file '$PWD/$f'"; done) -c copy output.mp4
Repetir mismo el video
# Windows ... in (start,step,end)
(for /l %i in (1,1,10) do @echo file './videoloop.mp4') > mylist.txt
ffmpeg -f concat -i list.txt -c copy output.mp4
# Bash
for i in {1..4}; do printf "file '%s'\n" input.mp4 >> list.txt; done
ffmpeg -f concat -i list.txt -c copy output.mp4
REFERENCIAS:
Una de las tareas básicas cuando haces scripting es verificar si una cadena esta o no vacía (puede venir de un parámetro, un input de usuario, etc). Lo hacemos con los operadores -n
y -z
Cadena vacia
#!/bin/bash
VAR=''
if [[ -z $VAR ]]; then
echo "String is empty."
fi
Cadena no vacia
#!/bin/bash
VAR='Linuxize'
if [[ -n $VAR ]]; then
echo "String is not empty."
fi
REFERENCIA: Linuxize
Conectarte a SQL Server Management Studio utilizando credenciales de windows diferentes, resulta muy util cuando te conectas por VPN a otras redes o dominios y requieres utilizar un usuario de otro dominio.
La clave es utilizar el comando runas
con los parámetros /user
y /netonly
desde un acceso directo al cual le indicaremos la ruta del ejecutable del SQL Server Management Studio añadiendo la opción nosplash
al final.
SSMS run as
C:\Windows\System32\runas.exe /user:domain\username /netonly "C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\Ssms.exe -nosplash"

Al ejecutar el SSMS usando el acceso directo, se abrirá una ventana de comando preguntando por tu contraseña del usuario REMOTO.
NOTA: Al momento de conectarte a un servidor en la ventana de conexión te aparecerán las credenciales de la maquina local, pero el usuario que en realidad se estará utilizando es el que hayas definido en el comando runas.
En ocasiones requerimos de eliminar el último commit realizado pero sin perder los cambios. Esto por muchas razones, en ocasiones es un commit prematuro, o se nos olvido agregar/eliminar un archivo, etc.
IMPORTANTE: Todo esto en nuestro entorno local sin haber realizado la publicación/sync a un origen remoto
git reset
: Mueve el branch actual hacia X commit
así que utilizamos HEAD~1
o "HEAD^"
(ambos son validos), ejemplo:
git reset
\$git reset HEAD~1
Incluyendo --soft
como parámetro dejara los archivos marcados para commit
Usando --soft
\$git reset --soft HEAD~1
Ahora bien en ocasiones solo requerimos hacer algún pequeño cambio o corregir el mensaje del commit para ello utilizamos git commit --amend
esto abre nuestro editor por lo que es importante tenerlo configurado
Usando amend
$git rm private.key
$git commit --amend

Escenario corregir el autor después de 2 commits
Corregir autor
$git reset HEAD~1
$git commit --amend --author="Jorge Anaya "
$git add .
$git commit -m "Fix bla bla bla"
La emergencia 🔔
Notificación 🔔, urge que te conectes al servidor remoto para ejecutar una script de SQL, te conectas y 💥

An error occurred while executing batch. Error message is: There is not enough space on the disk....
C:\Users\...\AppData\Local\Temp
para los resultados de un Query

Revisas archivos temporales, tu papelera, etc. luego ejecutas el asistente de limpieza de windows, nada ayuda, cuando mucho ganas unos pocos megas
WinDirStat
De pronto recuerdas esta maravillosa herramienta WinDirStat y la dejas analizar la unidad. Gracias a ella te das cuenta de dos carpetas que se estan comiendo tu espacio en el disco:
Limpia la papelera de todos los usuarios

1. Tus compañeros no son muy cuidadosos con su papelera de reciclaje
Vaciar la papelara de reciclaje de todos los usuarios
rd /s c:\$Recycle.Bin
Carpeta de instalación de Windows

2. Windows no es bueno limpiando su propio folder de instalaciones \Windows\Installer
En resumen, el problema es que cuando se desinstala una app, estas no eliminan de forma correcta sus paquetes de instalación/actualización
. Se supone que no deberíamos de tocar esta directorio, pero como en todo debemos de proceder con cuidado.
El fix en si es identificar los paquetes de instalación/actualización que pertenecen a aplicaciones que ya se desinstalaron del sistema.
Manualmente puedes ejecutar el siguiente VBA Script el cual genera un archivo que contiene los archivos que están instalados y que DEBEMOS de mantener en el sistema, lo demás puede ser borrado
VBA Script
'' Identify which patches are registered on the system, and to which
'' products those patches are installed.
''
'' Copyright (C) Microsoft Corporation. All rights reserved.
''
'' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
'' KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
'' IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
'' PARTICULAR PURPOSE.
'Option Explicit
Dim msi : Set msi = CreateObject("WindowsInstaller.Installer")
'Output CSV header
WScript.Echo "The data format is ProductCode, PatchCode, PatchLocation"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile("output.txt", True)
objFile.WriteLine "ProductCode, PatchCode, PatchLocation"
objFile.WriteLine ""
' Enumerate all products
Dim products : Set products = msi.Products
Dim productCode
For Each productCode in products
' For each product, enumerate its applied patches
Dim patches : Set patches = msi.Patches(productCode)
Dim patchCode
For Each patchCode in patches
' Get the local patch location
Dim location : location = msi.PatchInfo(patchCode, "LocalPackage")
objFile.WriteLine productCode & ", " & patchCode & ", " & location
Next
Next
WScript.Echo "Data written to output.txt, these are the registered objects and SHOULD be kept!"
Agradecimientos a Raymond.cc en su post se detallan ademas de el script anterior algunas otras formas de limpiar este directorio.

Suerte espero que estos dos tips te ayuden a recuperar valioso espacio en tu sistema.
Recientemente, vscode libero una extensión llamada Remote Development la cual te permite conectarte remotamente a proyectos en SSH, Containers y WSL.
Actualmente lo utilizo con un servidor local linux al cual me conecto via SSH.
-
Instalar la extensión Remote Development
-
Configurar el acceso al servidor via SSH
Para configurarlo desde Mac OS (~/rs_pub.id):
ssh-keygen -t rsa -b 4096 sh-copy-id jorgeanaya@host-fqdn-or-ip-goes-here
-
Configurar un acceso al servidor
SHIFT+CMD+P
- 3.1 Remote SSH: Connect to Host
- 3.2 Configure SSH Hosts …
- 3.3 Select ~/.ssh/config

Host server-name
HostName 192.168.1.10
User jorgeanaya
Cmder es un console emulator que proporciona todo aquello que le hace falta a la terminal por default de windows. Desde que la conocí siempre la cargo para todas mis instalaciones y equipos. Y como siempre olvido como configurar el vscode para que use esta terminal por default, aquí los pasos para lograrlo
-
Generar el siguiente archivo en C:\Cmder\vscode.bat
@echo off SET CurrentWorkingDirectory=%CD% SET CMDER_ROOT=C:\cmder CALL "%CMDER_ROOT%\vendor\init.bat" CD /D %CurrentWorkingDirectory%
-
En las configuraciones de vscode
Ctrl + ,
(settings.json)"terminal.integrated.shell.windows": "C:\\WINDOWS\\System32\\cmd.exe", "terminal.integrated.shellArgs.windows": ["/K", "C:\\cmder\\vscode.bat"], "terminal.integrated.fontFamily": "Fira Mono for Powerline",