Cómo seleccionar un rango de archivos por nombre parcial

Cómo seleccionar un rango de archivos por nombre parcial

Quiero seleccionar una variedad de archivos usando parte de su nombre para usarlos al copiar, mover, etc.

Lista de ejemplos aleatorios:

  1. alfa1a
  2. alfa2aa
  3. alfa3abc
  4. bravoA1
  5. bravoB2bb
  6. charlie
  7. deltaA1fdfd
  8. deltaA2dafa
  9. deltaA2222
  10. deltaC1
  11. ... (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: deltaBes la cadena más pequeña que viene después de todas las cadenas que comienzan con deltaA. Si quieres pasar deltaAcomo 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 sortpara 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 IFSy 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:

  1. Cree una lista que contenga los elementos 1adjuntos y los límites 0adjuntos.
  2. Ordena la lista.
  3. 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 en 1(es decir, los elementos) y sin el final 1.

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

información relacionada