🏋️ Ejercicios — Clase 02: Linux Básico para DevOps
Practicá todo lo que viste en la Clase 02. Cada ejercicio tiene pistas y solución. Intentá resolverlo solo antes de mirar.
👉 Usá el Playground ROXS en KillerCoda para practicar.
Ejercicio 1 — Bash Básico: Variables y sistema
Nivel: 🟢 Fácil
Creá un script mi-info.sh que:
- Cree una variable
MI_NOMBREcon tu nombre - Cree una variable
MI_EDADcon tu edad - Muestre "Hola, soy [nombre] y tengo [edad] años"
- Guarde en una variable
FECHA_HOYel resultado del comandodate - Guarde en
ARCHIVOS_AQUIcuántos archivos hay en el directorio actual - Muestre "Hoy es: [fecha]" y "Archivos aquí: [número]"
- Use un
ifpara verificar siMI_EDADes mayor o igual a 18 - Use un
forpara mostrar los números del 1 al 5
Ejemplo de salida:
Hola, soy RoxsRoss y tengo 25 años
Hoy es: sáb feb 21 15:30:00 -03 2026
Archivos aquí: 12
Eres mayor de edad
Número: 1
Número: 2
Número: 3
Número: 4
Número: 5
💡 Pistas
MI_NOMBRE="Juan"— sin espacios alrededor del=FECHA_HOY=$(date)— guardar resultado de comandoARCHIVOS_AQUI=$(ls | wc -l | tr -d ' ')— contar archivosif [ "$MI_EDAD" -ge 18 ]; then ... fifor i in 1 2 3 4 5; do ... done
✅ Solución
#!/bin/bash
# Ejercicio 01: Bash Básico
# Parte 1: Variables
MI_NOMBRE="RoxsRoss"
MI_EDAD=25
echo "Hola, soy $MI_NOMBRE y tengo $MI_EDAD años"
echo ""
# Parte 2: Comandos del sistema
FECHA_HOY=$(date)
ARCHIVOS_AQUI=$(ls | wc -l | tr -d ' ')
echo "Hoy es: $FECHA_HOY"
echo "Archivos aquí: $ARCHIVOS_AQUI"
echo ""
# Parte 3: Condicional
if [ "$MI_EDAD" -ge 18 ]; then
echo "Eres mayor de edad"
else
echo "Eres menor de edad"
fi
echo ""
# Parte 4: Loop
for i in 1 2 3 4 5; do
echo "Número: $i"
done
Ejercicio 2 — Variables avanzadas y aritmética
Nivel: 🟢 Fácil
Creá un script calculadora.sh que:
- Pida dos números al usuario con
read - Muestre el resultado de: suma, resta, multiplicación y división
- Muestre también las variables del sistema:
$USER,$HOME,$SHELL - Guarde la fecha en formato
YYYY-MM-DDen una variable
Ejemplo de salida:
🔢 Calculadora Bash
Ingresá el primer número: 15
Ingresá el segundo número: 4
━━━━━━━━━━━━━━━━━━━━
15 + 4 = 19
15 - 4 = 11
15 * 4 = 60
15 / 4 = 3
━━━━━━━━━━━━━━━━━━━━
Usuario: root
Home: /root
Shell: /bin/bash
Fecha: 2026-02-21
💡 Pistas
read -p "Texto: " VARIABLEpara pedir input- Bash solo hace matemática entera:
$((A + B)) date '+%Y-%m-%d'para formato de fecha
✅ Solución
#!/bin/bash
echo "🔢 Calculadora Bash"
read -p "Ingresá el primer número: " A
read -p "Ingresá el segundo número: " B
echo "━━━━━━━━━━━━━━━━━━━━"
echo " $A + $B = $((A + B))"
echo " $A - $B = $((A - B))"
echo " $A * $B = $((A * B))"
if [ "$B" -ne 0 ]; then
echo " $A / $B = $((A / B))"
else
echo " $A / $B = Error: división por cero"
fi
echo "━━━━━━━━━━━━━━━━━━━━"
echo "Usuario: $USER"
echo "Home: $HOME"
echo "Shell: $SHELL"
echo "Fecha: $(date '+%Y-%m-%d')"
Ejercicio 3 — Condicionales y verificaciones
Nivel: 🟢 Fácil
Creá verificar-sistema.sh que:
- Verifique si sos root (
$USERo$EUID) - Verifique si existen los archivos
/etc/hostsy/etc/passwd - Verifique si los comandos
curl,jq,gitydockerestán instalados - Muestre un saludo según la hora del día (mañana/tarde/noche)
- Muestre cada check con ✅ o ❌
💡 Pistas
[ "$USER" = "root" ]para verificar root[ -f "/etc/hosts" ]para verificar archivocommand -v curl &>/dev/nullpara verificar comandoHORA=$(date +%H)para obtener la hora
✅ Solución
#!/bin/bash
echo "🔍 Verificación del sistema"
echo ""
# Check 1: root
if [ "$USER" = "root" ]; then
echo "✅ Sos root"
else
echo "❌ No sos root (sos $USER)"
fi
# Check 2: archivos
for archivo in /etc/hosts /etc/passwd /etc/shadow; do
if [ -f "$archivo" ]; then
echo "✅ $archivo existe"
else
echo "❌ $archivo NO existe"
fi
done
# Check 3: comandos
for cmd in curl jq git docker; do
if command -v "$cmd" &>/dev/null; then
echo "✅ $cmd instalado"
else
echo "❌ $cmd NO instalado"
fi
done
# Check 4: saludo
HORA=$(date +%H)
if [ "$HORA" -ge 6 ] && [ "$HORA" -lt 12 ]; then
echo "🌅 Buenos días"
elif [ "$HORA" -ge 12 ] && [ "$HORA" -lt 18 ]; then
echo "☀️ Buenas tardes"
else
echo "🌙 Buenas noches"
fi
Ejercicio 4 — JSON con jq
Nivel: 🟡 Intermedio
Creá un archivo mi-pokemon.json con tu Pokémon favorito que tenga: nombre, tipo (array), nivel, hp y 3 movimientos. Luego:
- Crear el JSON a mano (válido)
- Verificar que es válido con
jq '.' - Extraer solo el nombre
- Extraer todos los movimientos
- Contar cuántos movimientos tiene
- Extraer el primer movimiento
- Crear un nuevo JSON con solo nombre y hp
Bonus: Creá un JSON con un equipo de 3 Pokémon (array de objetos) y usá jq para:
- Mostrar solo los nombres
- Mostrar el Pokémon con mayor nivel (
sort_by)
💡 Pistas
jq '.nombre' archivo.jsonpara extraer un campojq -r '.nombre'para sin comillasjq '.movimientos[]'para todos los elementos del arrayjq '.movimientos | length'para contarjq '{nombre: .nombre, hp: .hp}'para crear nuevo objetojq '.equipo | sort_by(.nivel) | last'para el de mayor nivel
✅ Solución
{
"nombre": "Charizard",
"tipo": ["fire", "flying"],
"nivel": 50,
"hp": 78,
"movimientos": ["flamethrower", "fly", "dragon-claw"]
}
# Verificar
jq '.' mi-pokemon.json
# Extraer nombre
jq -r '.nombre' mi-pokemon.json
# Todos los movimientos
jq '.movimientos[]' mi-pokemon.json
# Contar movimientos
jq '.movimientos | length' mi-pokemon.json
# Primer movimiento
jq '.movimientos[0]' mi-pokemon.json
# Nuevo JSON con nombre y hp
jq '{nombre: .nombre, hp: .hp}' mi-pokemon.json
Bonus — Equipo:
{
"equipo": [
{ "nombre": "Pikachu", "tipo": "electric", "nivel": 42 },
{ "nombre": "Charizard", "tipo": "fire", "nivel": 50 },
{ "nombre": "Mewtwo", "tipo": "psychic", "nivel": 70 }
]
}
# Solo nombres
jq '.equipo[].nombre' equipo.json
# Mayor nivel
jq '.equipo | sort_by(.nivel) | last' equipo.json
Ejercicio 5 — YAML
Nivel: 🟢 Fácil
Creá un archivo mi-pokemon.yaml con el mismo contenido que el JSON del ejercicio anterior. Recordá:
- Sin llaves ni comillas (en general)
- Listas con
- - Indentación con 2 espacios (nunca tabs)
Luego respondé:
- ¿Cuál formato te resulta más fácil de leer?
- ¿Cuál tiene más caracteres?
- ¿Qué pasa si la indentación está mal en YAML?
✅ Solución
nombre: Charizard
tipo:
- fire
- flying
nivel: 50
hp: 78
movimientos:
- flamethrower
- fly
- dragon-claw
Respuestas:
- YAML es más fácil de leer (menos ruido visual)
- JSON tiene más caracteres (llaves, comillas, comas)
- Si la indentación está mal en YAML, da error de parseo. YAML es estricto con la indentación.
Ejercicio 6 — curl + PokéAPI
Nivel: 🟡 Intermedio
Creá un script buscar-pokemon.sh que:
- Pida al usuario un nombre de Pokémon con
read - Convierta el nombre a minúsculas
- Haga un GET a
https://pokeapi.co/api/v2/pokemon/NOMBRE - Verifique el status code (200 = encontrado, 404 = no existe)
- Si existe, muestre: nombre, tipos, HP, peso y altura
- Guarde el resultado en un archivo
resultado.json
💡 Pistas
POKEMON=$(echo "$POKEMON" | tr '[:upper:]' '[:lower:]')para minúsculascurl -s -o /dev/null -w "%{http_code}" URLpara el status codecurl -s URL | jq '...'para extraer datosecho "$VARIABLE" > archivo.jsonpara guardar
✅ Solución
#!/bin/bash
API="https://pokeapi.co/api/v2/pokemon"
read -p "🔍 Nombre del Pokémon: " POKEMON
POKEMON=$(echo "$POKEMON" | tr '[:upper:]' '[:lower:]')
echo "Buscando $POKEMON..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$API/$POKEMON")
if [ "$HTTP_CODE" = "200" ]; then
RESPUESTA=$(curl -s "$API/$POKEMON")
NOMBRE=$(echo "$RESPUESTA" | jq -r '.name')
TIPOS=$(echo "$RESPUESTA" | jq -r '[.types[].type.name] | join(", ")')
HP=$(echo "$RESPUESTA" | jq '.stats[0].base_stat')
PESO=$(echo "$RESPUESTA" | jq '(.weight / 10)')
ALTURA=$(echo "$RESPUESTA" | jq '(.height / 10)')
echo ""
echo "✅ Pokémon encontrado!"
echo " Nombre: $NOMBRE"
echo " Tipos: $TIPOS"
echo " HP: $HP"
echo " Peso: ${PESO}kg"
echo " Altura: ${ALTURA}m"
echo "$RESPUESTA" | jq '{
nombre: .name,
tipos: [.types[].type.name],
hp: .stats[0].base_stat,
peso_kg: (.weight / 10),
altura_m: (.height / 10)
}' > resultado.json
echo ""
echo "📄 Guardado en resultado.json"
elif [ "$HTTP_CODE" = "404" ]; then
echo "❌ Pokémon '$POKEMON' no existe"
else
echo "⚠️ Error HTTP: $HTTP_CODE"
fi
Ejercicio 7 — Pokédex con loop y tabla
Nivel: 🟡 Intermedio
Creá pokedex-loop.sh que:
- Tenga un array con 5 Pokémon
- Recorra el array con un
for - Para cada uno, consulte la PokéAPI
- Muestre una tabla formateada con nombre, tipo y HP
- Al final, muestre cuántos Pokémon se consultaron
Ejemplo de salida:
╔══════════════════════════════════════════╗
║ 🔴 Mi Pokédex CLI ║
╚══════════════════════════════════════════╝
NOMBRE TIPO(S) HP
──────────── ───────────────── ────
pikachu electric 35
charizard fire, flying 78
mewtwo psychic 106
bulbasaur grass, poison 45
gengar ghost, poison 60
✅ Total consultados: 5
💡 Pistas
POKEMONES=("pikachu" "charizard" "mewtwo" "bulbasaur" "gengar")for p in "${POKEMONES[@]}"; do ... doneprintf "%-12s %-17s %4s\n" "$nombre" "$tipos" "$hp"para formato de tablaCONTADOR=$((CONTADOR + 1))para contar
✅ Solución
#!/bin/bash
API="https://pokeapi.co/api/v2/pokemon"
POKEMONES=("pikachu" "charizard" "mewtwo" "bulbasaur" "gengar")
CONTADOR=0
echo "╔══════════════════════════════════════════╗"
echo "║ 🔴 Mi Pokédex CLI ║"
echo "╚══════════════════════════════════════════╝"
echo ""
printf " %-12s %-17s %4s\n" "NOMBRE" "TIPO(S)" "HP"
echo " ──────────── ───────────────── ────"
for pokemon in "${POKEMONES[@]}"; do
info=$(curl -s "$API/$pokemon")
nombre=$(echo "$info" | jq -r '.name')
tipos=$(echo "$info" | jq -r '[.types[].type.name] | join(", ")')
hp=$(echo "$info" | jq '.stats[0].base_stat')
printf " %-12s %-17s %4s\n" "$nombre" "$tipos" "$hp"
CONTADOR=$((CONTADOR + 1))
done
echo ""
echo "✅ Total consultados: $CONTADOR"
Ejercicio 8 — Proyecto Mini Integrador
Nivel: 🔴 Avanzado
Creá proyecto-mini.sh que combine todo lo aprendido:
- Pida el nombre del usuario con
read - Obtenga la fecha con
date - Pida un Pokémon y lo busque en la PokéAPI
- Verifique que el Pokémon existe (status code)
- Extraiga datos con
jq(nombre, tipo, HP, ataque, defensa) - Genere un JSON completo con la info del usuario + el Pokémon
- Guarde el JSON en un archivo
- Muestre un resumen formateado en la terminal
💡 Pistas
- Usá
curl -s -o /dev/null -w "%{http_code}"para verificar - Guardá la respuesta en una variable para no hacer dos peticiones
- Usá
cat > archivo << EOF ... EOF(heredoc) para generar el JSON jq '.'para verificar que el JSON es válido
✅ Solución
#!/bin/bash
API="https://pokeapi.co/api/v2/pokemon"
echo "╔══════════════════════════════════════╗"
echo "║ 🎮 Proyecto Mini Integrador ║"
echo "╚══════════════════════════════════════╝"
echo ""
read -p "Tu nombre: " NOMBRE
read -p "Tu Pokémon favorito: " MI_POKEMON
MI_POKEMON=$(echo "$MI_POKEMON" | tr '[:upper:]' '[:lower:]')
FECHA=$(date '+%Y-%m-%d %H:%M:%S')
echo ""
echo "🔍 Buscando $MI_POKEMON..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$API/$MI_POKEMON")
if [ "$HTTP_CODE" != "200" ]; then
echo "❌ No se encontró '$MI_POKEMON' (HTTP $HTTP_CODE)"
exit 1
fi
RESP=$(curl -s "$API/$MI_POKEMON")
POKE_NOMBRE=$(echo "$RESP" | jq -r '.name')
POKE_TIPO=$(echo "$RESP" | jq -r '[.types[].type.name] | join(", ")')
POKE_HP=$(echo "$RESP" | jq '.stats[0].base_stat')
POKE_ATK=$(echo "$RESP" | jq '.stats[1].base_stat')
POKE_DEF=$(echo "$RESP" | jq '.stats[2].base_stat')
POKE_PESO=$(echo "$RESP" | jq '(.weight / 10)')
echo ""
echo "📋 Datos:"
echo " Entrenador: $NOMBRE"
echo " Pokémon: $POKE_NOMBRE"
echo " Tipo: $POKE_TIPO"
echo " HP: $POKE_HP"
echo " Ataque: $POKE_ATK"
echo " Defensa: $POKE_DEF"
echo " Peso: ${POKE_PESO}kg"
ARCHIVO="/tmp/reporte-${NOMBRE,,}-$(date +%Y%m%d).json"
POKE_JSON=$(echo "$RESP" | jq '{
nombre: .name,
id: .id,
tipos: [.types[].type.name],
hp: .stats[0].base_stat,
ataque: .stats[1].base_stat,
defensa: .stats[2].base_stat,
peso_kg: (.weight / 10),
altura_m: (.height / 10)
}')
jq -n \
--arg usuario "$NOMBRE" \
--arg fecha "$FECHA" \
--argjson pokemon "$POKE_JSON" \
'{
reporte: {
usuario: $usuario,
fecha: $fecha,
pokemon_favorito: $pokemon
}
}' | tee "$ARCHIVO"
echo ""
echo "✅ Reporte guardado en $ARCHIVO"
Ejercicio 9 — API Health Checker
Nivel: 🔴 Avanzado
Creá api-monitor.sh que:
- Tenga un array de URLs para verificar
- Para cada URL, haga un
curly mida el status code y tiempo de respuesta - Muestre una tabla con: URL, status code, tiempo
- Si alguna URL falla (status != 2xx), muestre ❌
- Al final, muestre un resumen: cuántas OK y cuántas fallidas
# URLs sugeridas para probar:
URLS=(
"https://pokeapi.co/api/v2/pokemon/1"
"https://httpbin.org/get"
"https://jsonplaceholder.typicode.com/posts/1"
"https://api.github.com"
"https://sitio-que-no-existe-12345.com"
)
💡 Pistas
curl -s -o /dev/null -w "%{http_code} %{time_total}" --max-time 5 URLawk '{print $1}'para extraer el primer campoprintfpara formatear la tabla- Verificar si el código está entre 200 y 399
✅ Solución
#!/bin/bash
echo "╔══════════════════════════════════════╗"
echo "║ 🌐 API Health Checker ║"
echo "╚══════════════════════════════════════╝"
echo ""
URLS=(
"https://pokeapi.co/api/v2/pokemon/1"
"https://httpbin.org/get"
"https://jsonplaceholder.typicode.com/posts/1"
"https://api.github.com"
"https://sitio-que-no-existe-12345.com"
)
printf " %-45s %6s %10s\n" "URL" "STATUS" "TIEMPO"
echo " ─────────────────────────────────────────── ────── ──────────"
OK=0
FAIL=0
for url in "${URLS[@]}"; do
RESULT=$(curl -s -o /dev/null -w "%{http_code} %{time_total}" --max-time 5 "$url" 2>/dev/null)
CODE=$(echo "$RESULT" | awk '{print $1}')
TIME=$(echo "$RESULT" | awk '{printf "%.2fs", $2}')
if [ "$CODE" -ge 200 ] 2>/dev/null && [ "$CODE" -lt 400 ] 2>/dev/null; then
printf " ✅ %-43s %6s %10s\n" "$url" "$CODE" "$TIME"
OK=$((OK + 1))
else
printf " ❌ %-43s %6s %10s\n" "$url" "$CODE" "$TIME"
FAIL=$((FAIL + 1))
fi
done
echo ""
echo " Resultado: ✅ $OK OK | ❌ $FAIL fallidas"
🏆 Desafío extra
Combiná todo en un script super-pokedex.sh que:
- Muestre un menú interactivo con
while trueycase - Opción 1: Buscar un Pokémon por nombre
- Opción 2: Buscar un Pokémon por número (ID)
- Opción 3: Comparar dos Pokémon (mostrar stats lado a lado)
- Opción 4: Buscar un equipo de 3 Pokémon y generar un JSON
- Opción 0: Salir
Todo con colores, manejo de errores y guardado en archivos.
🔗 Recursos
- Clase 02 — Linux Básico para DevOps
- Ejemplos Clase 02
- Playground ROXS
- PokéAPI — API gratuita para practicar
- ShellCheck — Validá tus scripts online
- ExplainShell — Entendé cualquier comando
- ▶️ Playlist del Bootcamp
- 🎮 Twitch — roxsross