Bash itera em pares de arquivos

3

Eu tenho um diretório com vários arquivos com nomes como a04x.txt , cada um com um arquivo b04y.txt correspondente. Eu preciso ser capaz de executar alguns comandos em cada par de arquivos e produzir um arquivo adicional c04z.txt para cada par.

Os números reais dos arquivos são bastante grandes e escassos, então simplesmente iterar todos os números de 1 a 99 ou algo assim não funcionará.

Atualmente, uso o seguinte para lidar com a tarefa, mas parece uma tarefa bastante comum que deveria haver uma maneira mais curta / melhor de fazê-lo:

for num in ./a*x.txt
do
  num="${num##*/a}"
  num="${num%x.txt}"

  my_command a${num}x.txt b${num}y.txt c${num}z.txt
done

Idealmente, eu também gostaria de ser avisado quando houver arquivos a${num}x.txt ou b${num}y.txt que não tenham um arquivo correspondente com o mesmo número. Eu também gostaria de uma maneira fácil de canalizar os conjuntos de arquivos para xargs ou parallel , para que possa processar vários conjuntos de arquivos simultaneamente.

Existe uma maneira melhor de fazer isso?

    
por AJMansfield 15.03.2016 / 04:33

2 respostas

2

  1. Uma abordagem seria fazer

    for afile in a*x.txt
    do
        bfile=${afile/a/b}; bfile=${bfile/x.txt/y.txt}
        cfile=${afile/a/c}; cfile=${cfile/x.txt/z.txt}
    
        my_command "$afile" "$bfile" "$cfile"
    done
    

    embora eu ache que não seja uma grande melhoria, e pode falhar em um caso patológico como um nome de arquivo de afoox.txtbarx.txt . Além disso, observe que isso é especificamente um recurso bash; pode não funcionar em outros shells compatíveis com POSIX (ao contrário de ## e % , que são especificados por POSIX).

  2. É simples dizer

        if [ -f "$bfile" ]
        then
            my_command "$afile" "$bfile" "$cfile"
        else
            echo Error
        fi
    

    para capturar outliers de arquivos a (por exemplo, a17x.txt sem correspondência b17y.txt ).

  3. Se você colocar

    for afile               # with no list, defaults to "$@"; i.e., the script’s arguments
    do
        bfile=${afile/a/b}; bfile=${bfile/x.txt/y.txt}
        cfile=${afile/a/c}; cfile=${cfile/x.txt/z.txt}
    
        if [ -f "$bfile" ]
        then
            my_command "$afile" "$bfile" "$cfile"
        else
            echo Error
        fi
    done
    

    em um script, você pode executar esse script com uma lista de anumx.txt nomes de arquivos como argumentos, e irá processá-los. Você pode então executar esse script por meio de xargs ou parallel .

  4. Verificando outliers de arquivos b (por exemplo, b42y.txt sem a42x.txt correspondente) como parte do processo acima não é simples, mas é fácil fazer um loop separado:

    for bfile in b*y.txt
    do
        afile=${bfile/b/a}; afile=${afile/y.txt/x.txt}
        if [ ! -f "$afile" ]
        then
            echo Error
        fi
    done
    
por 15.03.2016 / 08:27
2

O GNU paralelo tem uma maneira de fazer isso e executa os comandos em paralelo como um bônus:

$ parallel my_command {} \
                      {= s/a([0-9]+)x.txt/by.txt/ =} \
                      {= s/a([0-9]+)x.txt/cz.txt/ =} \
           ::: a*x.txt

Essas substituições são perl code. As quebras de linha são apenas para facilitar a leitura - este é um verso.

    
por 15.03.2016 / 17:48