Quiero seleccionar una variedad de archivos usando parte de su nombre para usarlos al copiar, mover, etc.
Lista de ejemplos aleatorios:
- alfa1a
- alfa2aa
- alfa3abc
- bravoA1
- bravoB2bb
- charlie
- deltaA1fdfd
- deltaA2dafa
- deltaA2222
- deltaC1
- ... (un montón de otros archivos)
Me gustaría ejecutar algún comando que vaya de alpha2* a deltaA*, lo que seleccionaría del 2 al 9 en la lista. De esta manera puedo seleccionar un rango basado en el nombre, no preocuparme por cuántos archivos hay realmente y no obtener extras.
Respuesta1
start=alpha2*
end=deltaA*
for file in *
do
if [[ $file > $start && ( $file < $end || $file == $end ) ]]; then
echo $file
fi
done
En lugar de eco, puedes almacenar los archivos en una matriz:
array+=($file)
Y luego use la matriz para copiar, mover los archivos... o simplemente ejecutar el comando dentro del bucle for.
Respuesta2
La programación Shell no es muy buena para las comparaciones lexicográficas. En bash, ksh o zsh, puedes usar el<
operador condicionalpara comparar dos cadenas (POSIX sh solo tiene comparaciones numéricas).
run_in_range () {
start_at="$1"; end_before="$2"; command="$3"; shift 3
for item do
if [[ ! ($x < $start_at) && ($x < $end_before) ]]; then
"$command" "$item"
fi
done
}
run_in_range alpha2 deltaB some_command *
Tenga en cuenta que utilicé el primer elemento excluido como segundo argumento: deltaB
es la cadena más pequeña que viene después de todas las cadenas que comienzan con deltaA
. Si quieres pasar deltaA
como argumento, puedes hacerlo de esta manera.
run_in_inclusive_range () {
start_at="$1"; end_on_prefix="$2"; command="$3"; shift 3
for item do
if [[ ! ($x < $start_at) && ($x < $end_on_prefix || $x == "$end_on_prefix"*) ]]; then
"$command" "$item"
fi
done
}
run_in_inclusive_range alpha2 deltaA some_command *
Este código no depende del orden del comodín: funciona incluso si la lista de elementos no está ordenada. Esto hace que el código sea más robusto, pero un poco más lento ya que tiene que recorrer toda la lista.
A continuación se presenta un enfoque alternativo que se basa únicamente en las funciones POSIX. Se utiliza sort
para hacer las comparaciones de cadenas. Este supone que no hay nuevas líneas en los elementos. Esta función restaura la configuración predeterminada para IFS
y globbing (es posible, pero molestamente difícil, restaurar la configuración ambiental).
run_in_range () {
IFS='
'; set -f
start_at="$1"; end_before="$2"; command="$3"; shift 3
set $({
printf '%s1\n' "$@"
printf '%s0\n' "$start_at" "$end_before"
} | sort | sed '/0$/,/0$/ s/1$//p')
unset IFS; set +f
for item do
"$command" "$item"
done
}
Explicación:
- Cree una lista que contenga los elementos
1
adjuntos y los límites0
adjuntos. - Ordena la lista.
- Imprima las líneas entre las dos líneas límite (las líneas que terminan en
0
), pero solo las líneas que terminan en1
(es decir, los elementos) y sin el final1
.
Respuesta3
Me parece que el problema es más sencillo de solucionar de lo que parece:
Supongamos que hay una manera de obtener la lista, un archivo por línea.
Para el directorio actual:
list="$(printf '%s\n' *)"
Simplemente agregue los límites a la lista y ordénela:
nl='
'
lim1="alpha2"
lim2="deltaA"
echo -- "$list""$nl""$lim1""$nl""$lim2" | sort
Luego comience a imprimir en lim1 (teniendo cuidado de que lim1 y lim2 no coincidan con un nombre de archivo existente) y deténgase en lim2.
Además, para simplificarlo, seleccione la cadena más corta que esté justo antes del archivo que desea imprimir y también la cadena más corta que se ordene justo después del último archivo que debe seleccionarse.
Lo que es útil es una lista exclusiva (una que no incluye los límites),
pero se proporcionan ambas soluciones.
Valores básicos:
list="$(printf '%s\n' *)"
lim1=alpha2
lim2=deltaA
Una solución con awk para una lista inclusiva:
echo "awk----------------inclusive"
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
awk -v lim1="$lim1" -v lim2="$lim2" '$0==lim1,$0==lim2{print}'
Una solución con awk para una lista exclusiva:
echo "awk----------------exclusive"
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
awk -v lim1="$lim1" -v lim2="$lim2" '$0==lim1{p=1;next};$0==lim2{p=0};p'
Una solución con el shell para una lista inclusiva:
echo "shell---------------inclusive"
show=0
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
while IFS="$nl" read -r line; do
[ "$line" = "$lim1" ] && show=1
[ "$show" = "1" ] && printf '%s\n' "$line"
[ "$line" = "$lim2" ] && show=0
done
Y una solución exclusiva para el shell:
show=0
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
while read -r line; do
[ "$line" = "$lim2" ] && show=0
[ "$show" = "1" ] && printf '%s\n' "$line"
[ "$line" = "$lim1" ] && show=1
done