Como pular subdiretórios em um loop for baseado apenas no nome?

2

Eu tenho um diretório organizado em subdiretórios da maneira de:

a1
b1
c1
c2
c3
d1

Estou tentando executar um loop que só é executado no subdiretório mais recente de uma determinada letra, ou seja, seria executado em a1 , b1 , c3 e d1 , mas ignore c1 e c2 . Meus esforços até agora giraram em torno de tentar comparar diretamente os valores numéricos dos nomes dos subdiretórios, mas isso não funcionou muito bem. Eu sou novo no bash, então peço desculpas se essa for uma correção simples que estou perdendo.

    
por Max H. 04.10.2015 / 17:03

4 respostas

2

Isso parece atingir sua meta

#!/bin/bash
for DIR in {a..z}
do
 GOOD=$(find ${DIR}[0-9] -type d -print -prune 2>/dev/null | tail -1)
 if [[ "$GOOD" ]]; then
  echo $GOOD
 fi
done

Exemplo de uso:

$ mkdir a1 b1 c1 c2 c3 d1
$ ./a.sh
a1
b1
c3
d1
$
    
por 04.10.2015 / 20:43
1

Eu tenho um pequeno canal complicado para fazer o seu trabalho:

#!/bin/bash
find . -maxdepth 1 -type d |
sed -e '/\.$/d' -e '/\.\.$/d' -e 's/\.\/\(.\)\(.\)/   /' |
sort -k1.1 -k2.1n |
awk 'BEGIN {last=""}
        {
                if (last == "") {
                        last = $1; number = $2
                } else if ($1 != last) {
                        printf "%s%s\n", last, number;
                        last = $1; number = $2
                } else {
                        number = $2
                }
        }
        END { printf "%s%s\n", last, number }
'

Você provavelmente terá que alterar os argumentos para find para obter exatamente o que deseja.

    
por 04.10.2015 / 20:49
1

Uma forma de agir no último de uma série é percorrer todos os elementos, lembrando-se do item anterior. Se você detectar que uma nova série está começando, atue no item anterior lembrado. Código não testado. Presumo que você esteja interessado apenas em diretórios cujo primeiro caractere não é um ponto e cujo último caractere é um dígito.

act_on () {
  …
}
previous_prefix=0
previous_name=
for x in *[0-9]/; do
  x=${x%/}
  digits=${x##*[!0-9]}
  prefix=${x%"$digits"}
  if [ "$prefix" != "$previous_prefix" ] && [ "$previous_prefix" != "0" ]; then
    # New prefix, so act on the previous element
    act_on "$previous_name"
  fi
  previous_prefix=$prefix
  previous_name=$x
done
act_on "$previous_name"

Tenha em atenção que foo9 é classificado após foo10 . Em zsh, use *[0-9](/on) para usar a classificação numérica, em que 9 é classificado antes de 10 . Se você não estiver usando o zsh, será necessária uma abordagem diferente. Uma maneira é agir apenas no primeiro item da série, mas em vez de agir sobre o item encontrado, use seu prefixo e determine o último elemento da série numérica.

act_on () {
  …
}
previous_prefix=0
previous_name=
for x in *[0-9]/; do
  x=${x%/}
  digits=${x##*[!0-9]}
  prefix=${x%"$digits"}
  if [ "$prefix" != "$previous_prefix" ]; then
    suffixes=
    for y in "$prefix"*; do
      suffixes="$suffixes
${y#"$prefix"}"
    done
    last_suffix=$(echo "$suffixes" | sort -n | tail -n 1)
    act_on "$prefix$last_suffix"
  fi
  previous_prefix=$prefix
  previous_name=$x
done
    
por 05.10.2015 / 01:22
0

Se os seus nomes de diretório são tão simples quanto você mostra (pelo menos, se não contiver espaço em branco ou outra estranheza), você poderia fazer:

for d in $(ls | sort -r | rev | uniq -s1 | rev); do echo "$d"; done

Altere o echo "$4d" para o que seu loop deve estar fazendo. No entanto, isso falhará se os nomes dos seus diretórios não forem exatamente como você os mostra, pois é vulnerável a todas as armadilhas da análise de ls. Ele trabalha primeiro listando os diretórios, classificando-os em ordem reversa, invertendo-os (assim a1 se torna 1a ), mantendo apenas linhas exclusivas, mas ignorando o primeiro caractere ( uniq -s1 ) e revertendo-as para obter o original nome de volta.

    
por 05.10.2015 / 12:57

Tags