É possível fazer um loop que crie uma variável a partir de cada nome de diretório dentro de um diretório quando os diretórios nem sempre são os mesmos?

É possível fazer um loop que crie uma variável a partir de cada nome de diretório dentro de um diretório quando os diretórios nem sempre são os mesmos?

Estou trabalhando em um script para ser executado em meu Synology DS 1019+ que pega todas as subpastas dentro de um determinado diretório, cria as subpastas em outro diretório e, em seguida, cria links físicos de todos os arquivos .mkv dentro das subpastas nas subpastas recém-criadas em o outro local. No entanto, se a subpasta já existir no segundo local, quero apenas criar o link físico.

Devido à natureza dos meus arquivos, a estrutura de pastas e os arquivos dentro dessas pastas serão diferentes para cada instância para a qual este script será usado. Existe uma maneira de fazer esse loop de script para que cada loop pegue a próxima pasta no Local A e execute o mkdir de cada subpasta e tarefa de link físico (para os arquivos dentro da referida subpasta)?

Aqui está o que tenho atualmente:

#! /bin/bash

echo "Enter Movie Collection:"
read MOVIE_DIR
echo "Enter Bonus Feature Disc: "
read BONUS_DIR

cd "/volume1/Plex/Movies/$MOVIE_DIR/"

for dir in */.; do

    if [[ ! -e "$dir"/*/ ]]; then
        mkdir "$dir"/*/
    fi

    if [[ ! -d "$dir"/*/ ]]; then
        ln /volume1/Plex/"Bonus Feature Discs"/$BONUS_DIR/*/*.mkv -t ./"$dir"/*/.
    fi
done

Não tenho muita habilidade quando se trata de loops, especialmente loops aninhados, por isso não sei por onde começar a solucionar isso. Atualmente, em vez de as pastas no local A serem replicadas (se ainda não existirem) no Local B, e os arquivos dentro das pastas no Local A serem vinculados, estou recebendo uma pasta vazia com o nome "_2X68P~X" dentro do Os diretórios principais do local B (por exemplo, em meu teste, em vez de "TEST 1" obter uma pasta "featurette" com um TEST.mkv com link físico dentro dela, acabei de obter aquela pasta de dados inúteis em "TEST 1" que está vazia.

Tentei usar basename e dirname, mas ainda não encontrei uma maneira de fazer um loop para que ele percorra cada pasta dentro de um diretório. Eu também tentei usarcd /the/directory/path/ "${PWD##*/}"

EDITAR: Ao longo de várias horas após postar isso, descobri uma solução. O código que eu tinha acima simplesmente não era logicamente correto, graças a muitas áreas que não eram específicas, o que significava que nada aconteceria. Acabei tendo que recomeçar do zero. Aqui está o código que acabei, e esse código faz o trabalho que preciso. Pode não ser o método mais elegante para fazer isso, mas funciona bem com base nos testes que executei.

#! /bin/bash

#Ask the user to input the directories of both the bonus disc's files and where the movies are located that the bonus disc's contents will be needed.
echo "Enter the name of the Movie Collection and press [ENTER]: "
read MOVIE_DIR
echo "Enter the name of the Bonus Feature Disc and press [ENTER]: "
read BONUS_DIR


#This goes to the location of the bonus disc specified by the end user. I believe this part is necessary for creating the text document below, but it might not be.
cd "/volume1/Plex/Bonus Feature Discs/$BONUS_DIR/" || return

#This creates a text document that has each directory within the specified Bonus Disc directory as a separate line  
    ls -d -- */ >> /volume1/Plex/"Bonus Feature Discs"/output.txt
    echo ls  -d -- */


#This goes to the movie directory specified by the end user. This cd is definitely required
cd "/volume1/Plex/Movies/$MOVIE_DIR/" || return


#the for loop loops through every movie that resides in the movie collection folder
 for dir in *; do

    #this while loop reads the text document and will use each line from the document as a variable.
    while IFS=' ' read -r line; do
        name="$line"

        #A directory with the name of the line of the text document will be created in each movies directory only if it doesn't already exist
        mkdir -p "$dir/$name"

        #this will create the hard links to every single video that resides in the folders within the bonus features disc into the corresponding folders for each movie
        if [[ ! -e "/volume1/Plex/Movies/$MOVIE_DIR/$name" ]]; then 
            ln "/volume1/Plex/Bonus Feature Discs/$BONUS_DIR/$name"*.mkv -t ./"$dir/$name"
        fi

    done < "/volume1/Plex/Bonus Feature Discs/output.txt"
 done

echo "Linking Completed"
echo $dir

#finally, once all the work is complete, the script will delete the text document that is no longer needed
rm "/volume1/Plex/Bonus Feature Discs/output.txt"

Responder1

Usando rsynce assumindo que você deseja vincular todos .mkvos arquivos dentro ou abaixo do sourcediretório para o targetdiretório, incluindo a criação de todos os diretórios (mesmo que estejam vazios de .mkvarquivos).

rsync --archive --link-dest="$PWD/source" \
    --include='*/' \
    --include='*.mkv' \
    --exclude='*' \
    source/ target

A opção --archive( ) preserva os metadados do arquivo e também aciona a cópia recursiva de diretórios. A opção fornece um diretório a partir do qual os arquivos existentes podem ser vinculados diretamente ao arquivo . As opções selecionam diretórios e arquivos, enquanto a opção final ignora tudo que ainda não foi selecionado com .-arsync--link-destrsynctarget--include.mkv--exclude--include

Para excluir diretórios vazios que podem ser gerados em target, você pode usar findassim:

find target -type d -empty -delete

... assumindo que você findimplementa o -emptyteste e a -deleteação.

Exemplo:

source
|-- dir1
|   |-- file1.mkv
|   |-- file1.txt
|   |-- file2.mkv
|   `-- file2.txt
|-- dir2
|   |-- file1.mkv
|   |-- file1.txt
|   |-- file2.mkv
|   `-- file2.txt
|-- dir3
|   |-- file1.mkv
|   |-- file1.txt
|   |-- file2.mkv
|   `-- file2.txt
`-- dir4
    `-- file1.doc

O rsynccomando acima é executado e targetse torna ...

target
|-- dir1
|   |-- file1.mkv
|   `-- file2.mkv
|-- dir2
|   |-- file1.mkv
|   `-- file2.mkv
|-- dir3
|   |-- file1.mkv
|   `-- file2.mkv
`-- dir4

Apenas mostrando que os arquivos têm links físicos (mesmo número de inode, contagem de links é 2):

$ ls -l -i source/dir1/file1.mkv target/dir1/file1.mkv
3118217 -rw-r--r--  2 kk  kk  0 Apr 10 17:03 source/dir1/file1.mkv
3118217 -rw-r--r--  2 kk  kk  0 Apr 10 17:03 target/dir1/file1.mkv

Excluindo diretórios vazios:

$ find target -type d -empty -delete
$ tree target
target
|-- dir1
|   |-- file1.mkv
|   `-- file2.mkv
|-- dir2
|   |-- file1.mkv
|   `-- file2.mkv
`-- dir3
    |-- file1.mkv
    `-- file2.mkv

Responder2

Presumo que você de alguma forma atribua valores às duas variáveis dirA​​​​e dirB("Local A" e "segundo local" em sua descrição).

Você faz um loop em todos os arquivos em dirA.

Cada vez que o arquivo dirAé um diretório, você cria outro diretório com o mesmo nome dirB. A --parentopção evita mkdirgerar um erro se o novo diretório já existir, eliminando a necessidade de verificar a existência do novo diretório.

O interno ifverifica se existem arquivos .mkv e ignora o diretório, caso contrário.

Este script funciona para um único nível de diretório: se você tiver diretórios dentro de diretórios, eles não serão visualizados.

    #! /bin/bash

    dirA=...
    dirB=...

    cd "$dirA"

    for dir in *; do
      if [ -d "$dir" ]; then
         names=$(find "$dir" -maxdepth 1 -name '*.mkv')
         if [ "$names" ]; then
           mkdir --parent "$dirB/$dir"
           ln "$dir"/*.mkv "$dirB/$dir"
         fi
      fi
    done

informação relacionada