Eu tenho um servidor que recebe um arquivo por cliente todos os dias em um diretório. Os nomes de arquivos são construídos da seguinte maneira:
uuid_datestring_other-data
Por exemplo:
d6f60016-0011-49c4-8fca-e2b3496ad5a7_20160204_023-ERROR
uuid
é um formato padrão uuid.datestring
é a saída dedate +%Y%m%d
.other-data
é variável em tamanho, mas nunca conterá um sublinhado.
Eu tenho um arquivo do formato:
#
d6f60016-0011-49c4-8fca-e2b3496ad5a7 client1
d5873483-5b98-4895-ab09-9891d80a13da client2
be0ed6a6-e73a-4f33-b755-47226ff22401 another_client
...
Preciso verificar se todo uuid listado no arquivo tem um arquivo correspondente no diretório, usando o bash.
Cheguei até aqui, mas sinto que estou vindo da direção errada usando uma instrução if e que preciso percorrer os arquivos no diretório de origem.
As variáveis source_directory e uuid_list foram atribuídas anteriormente no script:
# Check the entries in the file list
while read -r uuid name; do
# Ignore comment lines
[[ $uuid = \#* ]] && continue
if [[ -f "${source_directory}/${uuid}*" ]]
then
echo "File for ${name} has arrived"
else
echo "PANIC! - No File for ${name}"
fi
done < "${uuid_list}"
Como devo verificar se os arquivos da minha lista existem no diretório? Eu gostaria de usar a funcionalidade bash, tanto quanto possível, mas não sou contra o uso de comandos, se necessário.
command-line
bash
scripts
Arronical
fonte
fonte
Respostas:
Percorra os arquivos, crie uma matriz associativa sobre os uuids contidos em seus nomes (usei a expansão de parâmetros para extrair o uuid). Leia a lista, verifique a matriz associativa de cada uuid e relate se o arquivo foi gravado ou não.
fonte
cd
entrar no diretório dentro do script, mas me perguntei apenas para obter conhecimento.file=${file##*/}
.Aqui está uma abordagem mais "bashy" e concisa:
Observe que, embora o acima seja bonito e funcione bem para alguns arquivos, sua velocidade depende do número de UUIDs e será muito lento se você precisar processar muitos. Se for esse o caso, use a solução do @ choroba ou, para algo realmente rápido, evite o shell e chame
perl
:Apenas para ilustrar as diferenças de horário, testei minha abordagem de bash, choroba e meu perl em um arquivo com 20000 UUIDs, dos quais 18001 tinham um nome de arquivo correspondente. Observe que cada teste foi executado redirecionando a saída do script para
/dev/null
.Minha festança (~ 3,5 min)
Choroba (festa, ~ 0.7 seg)
Meu perl (~ 0,1 s):
fonte
cd
entrar no diretório do script, mas existe um método pelo qual o caminho do arquivo possa ser incluído na pesquisa?${source_directory}
como estava usando no seu script."$2"
e passe-o para o script como um segundo argumento.Este é o Bash puro (ou seja, sem comandos externos), e é a abordagem mais coincidente em que consigo pensar.
Mas o desempenho não é realmente muito melhor do que o que você tem atualmente.
Ele lerá cada linha de
path/to/file
; para cada linha, ele armazenará o primeiro campo$uuid
e imprimirá uma mensagem se um arquivo correspondente ao padrão nãopath/to/directory/$uuid*
for encontrado:Ligue com
path/to/script path/to/file path/to/directory
.Saída de amostra usando o arquivo de entrada de amostra na pergunta em uma hierarquia de diretórios de teste que contém o arquivo de amostra na pergunta:
fonte
A idéia aqui não é se preocupar em relatar erros que o shell reportará para você. Se você tentar
<
abrir um arquivo que não existe, seu shell irá reclamar. De fato, ele acrescentará o número do seu script$0
e da linha em que o erro ocorreu à saída do erro quando ocorrer ... Essas são boas informações que já são fornecidas por padrão - portanto, não se preocupe.Você também não precisa colocar o arquivo linha por linha assim - pode ser muito lento. Isso expande tudo em um único tiro para uma matriz de argumentos delimitada por espaço em branco e lida com dois de cada vez. Se seus dados forem consistentes com o seu exemplo,
$1
sempre será o seu uuid e$2
será o seu$name
. Sebash
pode abrir uma correspondência para o seu uuid - e apenas existe uma dessas correspondências -, issoprintf
acontece. Caso contrário, isso não ocorre e o shell grava diagnósticos no stderr sobre o porquê.fonte
unset IFS
garante que$(cat <uuid_file)
seja dividido em espaço em branco. Os shells dividem-se de maneira$IFS
diferente quando são compostos apenas por espaços em branco ou não estão definidos. Essas expansões de divisão nunca têm campos nulos porque todas as seqüências de espaço em branco permanecem como apenas um delimitador de campo. Contanto que existam apenas dois campos separados por espaços não brancos em cada linha, deve funcionar, eu acho. debash
qualquer maneira.set -f
garante que a expansão não citada não seja interpretada para globs e o conjunto + f garante que os globs posteriores sejam.<>
porque isso cria um arquivo inexistente.<
relatará como eu pretendia. o possível problema com isso - e a razão pela qual eu usei incorretamente<>
- é que, se for um arquivo de pipe sem um leitor ou como um char dev com buffer de linha, ele travará. isso poderia ser evitado manipulando a saída de erro mais explicitamente e executando[ -f "$dir/$1"* ]
. estamos falando de uuids aqui e, portanto, ele nunca deve se expandir para mais do que um único arquivo. é meio legal como ele relata os nomes dos arquivos com falha para stderr assim.<>
, ainda seria utilizável dessa maneira ...<>
é melhor se a glob puder se expandir para um diretório, porque em um linux a leitura / gravação será falhe e diga - isso é um diretório.bash
só aceitará um glob de redirecionamento se ele corresponder apenas a um arquivo. vejaman bash
em REDIRECÇÃO.A maneira como eu abordaria isso é obter os uuids do arquivo primeiro e depois usar
find
Para facilitar a leitura,
Exemplo com uma lista de arquivos em
/etc/
, procurando nomes de arquivos passwd, group, fstab e THISDOESNTEXIST.Como você mencionou que o diretório é plano, você pode usar a
-printf "%f\n"
opção para imprimir o próprio nome do arquivoO que isso não faz é listar os arquivos ausentes.
find
A pequena desvantagem é que ele não informa se não encontra um arquivo, apenas quando corresponde a algo. O que se poderia fazer, no entanto, é verificar a saída - se a saída estiver vazia, temos um arquivo ausenteMais legível:
E aqui está como ele funciona como um pequeno script:
Pode-se usar
stat
como alternativa, já que é um diretório simples, mas o código abaixo não funcionará recursivamente para subdiretórios se você decidir adicioná-los:Se pegarmos a
stat
ideia e executá-la, poderíamos usar o código de saída stat como indicação para a existência ou não de um arquivo. Efetivamente, queremos fazer o seguinte:Exemplo de execução:
fonte