A modo de proyecto para este curso, decidí copiar la idea del profesor de un script que ordenar todos los screen shots en carpetas respecto a cuando fueron creados.
Mi primer idea de solución lo dibuje en el siguiente diagrama.

El primer paso es determinar los meses en que los archivos fueron creados, para ello use:
ls -l
Resultado :
..
-rw-r--r--@ 1 johan staff 104354 29 Aug 12:15 WhatsApp Image 2019-08-29 at 12.14.52 PM.jpeg
-rw-r--r--@ 1 johan staff 23758 24 Jul 10:14 switch.png
drwxr-xr-x 2 johan staff 64 17 Oct 21:05 test
-rw-r-x-w-@ 1 johan staff 0 17 Oct 20:22 text.txt
Luego con grep y una expresión regularfiltro solo archivos, mi idea es que si el resultado del ls -l no comienza por con ‘-’ no lo tomo. Y la defino de la siguiente manera:
ls -l | grep "^-.*$"
Resultado:
..
-rw-r--r--@ 1 johan staff 104354 29 Aug 12:15 WhatsApp Image 2019-08-29 at 12.14.52 PM.jpeg
-rw-r--r--@ 1 johan staff 23758 24 Jul 10:14 switch.png
-rw-r-x-w-@ 1 johan staff 0 17 Oct 20:22 text.txt
Luego con awk y un print filtro los meses en que los archivos cuando fueron creados. En este link encontré una forma de hacerlo.
ls -l | grep "^-.*$" | awk '{print $7}'
Resultado:
..
Sep
Sep
Oct
Oct
Oct
Jul
Sep
Sep
Sep
Sep
Aug
Jul
Oct
..
Luego para filtrar todos los archivos repetidos con sort -u puedo hacerlo. De este link tome la idea:
ls -l | grep "^-.*$" | awk '{print $7}' | sort -u
Y para guardar el resultado de este comando en una variable de bash en este link que se hace así:
MONTHS=$(ls -l | grep "^-.*$" | awk '{print $7}' | sort -u)
Y además cree este comando para determinar el año de creación de los archivos:
YEAR=$(date | awk '{print $4}')
Para formalizar el script cree un archivo llamado “sort_screen_shots.sh” y hasta este punto se ve así :
#!/bin/bash
MONTHS=$(ls -l | grep "^-.*$" | awk '{print $7}' | sort -u)
YEAR=$(date | awk '{print $4}')
<h3>Paso 2:</h3>
El segundo paso es la creación de las carpetas donde se van a ordenar los archivos, para ello use un ciclo for el cual es muy parecido al de Python, acá encontré como se define. Y aplicado a mi proyecto se ve de la siguiente manera:
#!/bin/bash
MONTHS=$(ls -l | grep "^-.*$" | awk '{print $7}' | sort -u)
YEAR=$(date | awk '{print $4}')
for i in $MONTHS
do
mkdir -pv "$i-$YEAR"
done
Resultado:
Aug-2019
Jul-2019
Oct-2019
Sep-2019
El -p e es para que si la carpeta ya esta no genere error y el -v es para que muestre en terminal lo que esta creando.
El tercer paso es mover los archivos a sus carpetas respectivas. Para ello dentro del ciclo for aplique una expresión regular que filtrara los archivos que fueron creados en ese respectivo mes y luego con awk tomo los nombres para moverlos a las carpetas. El script se ve de la siguiente manera.
#!/bin/bash
MONTHS=$(ls -l | grep "^-.*$" | awk '{print $7}' | sort -u)
YEAR=$(date | awk '{print $4}')
for i in $MONTHS
do
mkdir -p $i-$YEAR
REGEX="^-.*\s${i}\s\d\d:\d\d.*$"
FILES_TO_MOVE=$(ls -l | grep $REGEX | awk '{print $9}')
mv -v $FILES_TO_MOVE $i-$YEAR/
done
Donde la expresión regular se puede entender de la siguiente manera:

Si no entendiste la expresiones regular te invito a hacer el curso de Regex (Excelente curso!)
Pero el script aún tiene un error, por que los nombres se generan con espacios en MacOs , entonces awk nos retorna solo la primera parte del nombre de los archivos y con esto comando mv genera problemas. Buscando por internet varias formas de solucionar esto 1, 2, pero creo la mejor solución seria cambiar los " " por “_” en los nombres y así lo implemente:
# rename files
for f in "$(ls | grep '\s')"
do
mv "$f" "${f// /_}"
done
Esta idea la tome de este foro y muestra una funcionalida de bash llamada “Parameter expansion” (MUY Interesante).
Y al final mi scrip quedo de la siguiente manera:
Versión final:
#!/bin/bash
# path of the screen shots folder
PATH_SCREEN_SHOT="$HOME/Documents/screen-shots"
SORT_DATE=$(date)
echo "Date of sort $SORT_DATE"
# move to the main path
echo "move to $PATH_SCREEN_SHOT"
cd $PATH_SCREEN_SHOT
# rename files
for f in "$(ls | grep '\s')"
do
if [ -n "$f" ]
then
mv -v "$f" "${f// /_}"
fi
done
# move files
MONTHS=$(ls -l | grep '^-.*$' | awk '{print $7}' | sort -u)
YEAR=$(date | awk '{print $4}' )
echo 'Beginning To Move'
for i in $MONTHS
do
if [ -n "$i" ]
then
mkdir -pv $i-$YEAR
REGEX="^-.*\s${i}\s\d\d:\d\d.*$"
FILES_TO_MOVE=$(ls -l | grep $REGEX | awk '{print $9}')
mv -v $FILES_TO_MOVE $i-$YEAR/
fi
done
Agregue un if en cada for para evitar problemas cuando no se encuentren archivos el script no genere errores, además echo y -v para crear un “.log” de la ejecución.
El cuarto paso es agregar el script al crontap, esto lo hacemos atraves de el comando crontab -e y creamos nuestra configuración de días de ejecución cómo en la clase lo mostraron.
0 0 */15 * * /Users/johan/.my_crontap_scripts/sort_screen_shots.sh > /Users/johan/.my_crontap_scripts/sort.log
<h3>Lecciones aprendidas</h3>
Antes de hacer este mini-proyecto pensaba que era algo sencillo que con mi experiencia en Linux (4 años usando Ubuntu) no me tomaría mas de una tarde, pero me tomo 5 días con un promedio de tres horas de trabajo por día. Pero valió el tiempo invertido por que aprendí muchísimo en el procesó. Si sabes una forma mas sencilla de solucionar esté problema déjenme lo saber en los comentarios o Twitter . Gracias por leer este post 🤙🏾.
<h3>Referencias</h3>- Parameter expansion
- grep regex whitespace behavior
- Capturing Groups From a Grep RegEx
- How do I split a string on a delimiter in Bash?
- Redirecting command output to a variable in bash fails
- How to use the awk to print the output
- How to check if a pipe is empty and run a command on the data if it isn’t?
- Pass the output of previous command to next as an argument
- Extracting a part of String using grep/sed/awk
- Shell script change directory with variable
- How to replace spaces in file names using a bash script
- Iterate over a list of files with spaces
- The while loop
- https://regexr.com
- If Statements!
- Meaning of “[: too many arguments” error from if [] (square brackets)
Introducción a Terminal y Línea de Comandos 2018
COMPARTE ESTE ARTÍCULO Y MUESTRA LO QUE APRENDISTE

