Como selecionar um intervalo de arquivos pelo nome parcial

1

Eu quero selecionar um intervalo de arquivos usando parte do nome deles para serem usados com cópia, movimentação, etc.

Lista de exemplos aleatórios:

  1. alpha1a
  2. alpha2aa
  3. alpha3abc
  4. bravoA1
  5. bravoB2bb
  6. charlie
  7. deltaA1fdfd
  8. deltaA2dafa
  9. deltaA2222
  10. deltaC1
  11. ... (um monte de outros arquivos)

Gostaria de executar algum comando que vai de alpha2 * a deltaA *, que selecionaria 2-9 na lista. Dessa forma, posso selecionar um intervalo com base no nome, não se preocupar com quantos arquivos realmente existem e não obter extras.

    
por Brandon 25.09.2016 / 02:25

3 respostas

1

start=alpha2*
end=deltaA*

for file in *
do
    if [[ $file > $start && ( $file < $end || $file == $end ) ]]; then
        echo $file
    fi
done

Em vez de echo, você pode armazenar os arquivos em uma matriz:

array+=($file)

E, em seguida, use o array para copiar, mover os arquivos ... ou simplesmente executar o comando dentro do loop for.

    
por 25.09.2016 / 06:12
1

A programação shell não é muito boa em comparações lexicográficas. No bash, ksh ou zsh, você pode usar o operador condicional < para comparar duas strings (POSIX sh só tem comparações 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 *

Observe que usei o primeiro item excluído como segundo argumento: deltaB é a menor string que vem depois de todas as sequências que começam com deltaA . Se você quiser passar deltaA como argumento, você pode fazer desta forma.

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 não depende da ordem do curinga: ele funciona mesmo se a lista de itens não estiver classificada. Isso torna o código mais robusto, mas um pouco mais lento, já que ele precisa passar por toda a lista.

Aqui está uma abordagem alternativa que depende apenas dos recursos POSIX. Ele usa sort para fazer as comparações de string. Este assume que não há novas linhas nos itens. Esta função restaura a configuração padrão para IFS e globbing (é possível, mas irritantemente difícil restaurar a configuração do ambiente).

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
}

Explicação:

  1. Crie uma lista contendo os itens com 1 acrescentados e os limites com 0 acrescentados.
  2. Classifique a lista.
  3. Imprima as linhas entre as duas linhas limítrofes (as linhas que terminam em 0 ), mas apenas as linhas que terminam com 1 (ou seja, os itens) e sem o coeficiente 1 .
por 26.09.2016 / 02:29
0

Parece-me que o problema é mais simples de resolver do que parece:

Vamos supor que haja uma maneira de obter a lista, um arquivo por linha.
Para o diretório atual:

list="$(printf '%s\n' *)"

Basta anexar os limites à lista e classificá-lo:

nl='
'
lim1="alpha2"
lim2="deltaA"

echo -- "$list""$nl""$lim1""$nl""$lim2" | sort

Em seguida, inicie a impressão no lim1 (cuidando que lim1 e lim2 não correspondam a um nome de arquivo existente) e pare no lim2.

Além disso, para simplificar, selecione a string mais curta anterior ao arquivo que deseja imprimir e também a string mais curta que ordena logo após o último arquivo que precisa ser selecionado.

O que é útil é uma lista exclusiva (uma que não inclua os limites),
mas ambas as soluções são fornecidas.

Valores básicos:

list="$(printf '%s\n' *)"

lim1=alpha2
lim2=deltaA

Uma solução com o awk para uma lista abrangente:

echo "awk----------------inclusive"
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
    awk -v lim1="$lim1" -v lim2="$lim2" '$0==lim1,$0==lim2{print}'

Uma solução com o awk para uma 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'

Uma solução com o shell para uma lista abrangente:

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

E uma solução exclusiva para o 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
    
por 27.09.2016 / 01:36