Symlinks de acordo com a profundidade do diretório

1

Eu tenho a seguinte estrutura de diretórios que preciso criar e, ao fazer isso, também preciso criar os arquivos zip que são links simbólicos.

/a/2015-08-17/a.zip  
/a/2015-08-17/a_b/a_b.zip
/a/2015-08-17/a_b/a_b_c/a_b_c.zip
/a/2015-08-17/a_b/a_b_c/a_b_c_1/a_b_c_1.zip 
/a/2015-08-17/a_b/a_b_c/a_b_c_2/a_b_c_2.zip
/a/2015-08-17/a_b/a_b_c/a_b_c_3/a_b_c_3.zip

Os arquivos 1stdepth.zip, 2nddepth.zip etc existem e são os mesmos a cada vez. Eu recebo a estrutura de diretórios como a acima e preciso criar os seguintes links sym de acordo com a profundidade da estrutura de diretórios.

ln -s 1stdepth.zip /a/2015-08-17/a.zip  
ln -s 2nddepth.zip /a/2015-08-17/a_b/a_b.zip
ln -s 3rddepth.zip /a/2015-08-17/a_b/a_b_c/a_b_c.zip
ln -s 4thdepth.zip /a/2015-08-17/a_b/a_b_c/a_b_c_1/a_b_c_1.zip  
ln -s 4thdepth.zip /a/2015-08-17/a_b/a_b_c/a_b_c_2/a_b_c_2.zip
ln -s 4thdepth.zip /a/2015-08-17/a_b/a_b_c/a_b_c_3/a_b_c_3.zip

Como eu iria fazer isso em um script? Devo contar o caminho e perceber de acordo com que profundidade eu estou? Existe uma maneira mais inteligente de fazer isso? Estou usando o Solaris.

    
por Lara 08.10.2015 / 13:35

4 respostas

1

Veja como resolvi meu problema. Pode não ser o melhor caminho, mas eu precisava de uma solução rápida. Talvez alguém precise disso

#!/usr/bin/bash

function main()
{
[ $# -ne 1 ] && { echo "Usage: $0 file"; exit 1; }
depth1=/a.zip
depth2=/b.zip
depth2_special=/special.zip
depth3=/c.zip
depth4=/d.zip

cat $1 | while read line; do
#Count the number of occurences of / in each line 
x='echo $line |awk -F/ '{c += NF - 1} END {print c}''
if [ $x -eq 13 ] ;then echo "The path has 13 / " 
    ln -sf $depth4 $line 
else
    if [ $x -eq 12 ] ;then echo "The path has 12 /"
        ln -sf $depth3 $line 
    else
        if [ $x -eq 11 ] ; then echo "The path has 11 /"
            #If there is no _ in the file path treat it as a special case
            underscore='echo $line |awk -F_ '{c += NF - 1} END {print c}''
                if [ $underscore -eq 1 ] ; then
                    ln -sf $depth2 $line
                else
                    ln -sf $depth2_special $line 
                fi; 
        else
            if [ $x -eq 10 ] ;then echo "The path has 10 /" 
                ln -sf $depth1 $line 
            else
                echo "Error - the path is not correct" 
            fi;
        fi;
    fi; 
fi;
done

}

main "$@"
    
por 08.10.2015 / 17:09
0

Eu acho que o comando tree no linux pode ajudá-lo a obter a profundidade do Diretório e colocar o seu problema em algum lugar.

Ps: Eu não tenho idéia sobre o Solarix, mas no Debian temos tree command

    
por 08.10.2015 / 14:15
0

Respire fundo e resolva os problemas um de cada vez, procurando soluções simples.

Primeiro, tudo o que precisamos fazer acontece no diretório /a/2015-08-17 . Então, vamos mudar para esse diretório.

cd /a/2015-08-17

Agora, precisamos criar vários links simbólicos com base nos diretórios existentes. (Eu suponho que os diretórios já existem desde que você não explique como determinar quais diretórios criar.) O comando básico para fazer algo em todos os subdiretórios do diretório atual é

find . -type d …

O que precisamos fazer é criar um link simbólico baseado na profundidade do diretório. O nome do link simbólico é o nome base do diretório mais .zip no final (exceto que é a.zip no topo) e o destino é calculado a partir da profundidade. Vamos fazer o nome do link primeiro.

Vamos precisar fazer alguns cálculos em cada diretório encontrado por find , então precisamos invocar um shell.

find . -type d -exec sh -c '???' {} \;

O caminho para o diretório (começando com . ) está disponível no script como $0 . Se $0 for apenas . , precisamos criar a.zip .

find . -type d -exec sh -c '
    if [ "$0" = "." ]; then ln -s 1stdepth.zip a.zip; fi
' {} \;

Caso contrário, precisamos extrair o nome base do diretório. Isso pode ser feito com a construção de expansão do parâmetro do shell ${0##*/} (valor de $0 menos o prefixo mais longo que termina em / ):

find . -type d -exec sh -c '
    if [ "$0" = "." ]; then
      ln -s 1stdepth.zip a.zip
    else
      ln -s ???depth.zip "$0/${0##*/}.zip"
    fi
' {} \;

Agora precisamos calcular a profundidade. Esse é o número de barras em $0 , mais uma desde que a raiz tenha profundidade 1. Uma maneira de contar o número de barras é remover todos os outros caracteres e contar a duração do que resta.

find . -type d -exec sh -c '
    depth=$(printf %s "$0" | tr -dc / | wc -c)
    if [ "$0" = "." ]; then
      ln -s 1stdepth.zip a.zip
    else
      ln -s $((depth+1))???depth.zip "$0/${0##*/}.zip"
    fi
' {} \;

Quase lá; precisamos calcular o sufixo para obter o 1º, 2º, etc.

find . -type d -exec sh -c '
    depth=$(($(printf %s "$0" | tr -dc / | wc -c) + 1))
    if [ "$0" = "." ]; then
      ln -s 1stdepth.zip a.zip
    else
      case $depth in
        *1?) suffix=th;;
        *1) suffix=st;;
        *2) suffix=nd;;
        *3) suffix=rd;;
        *) suffix=th;;
      esac
      ln -s "${depth}${suffix}depth.zip" "$0/${0##*/}.zip"
    fi
' {} \;
    
por 09.10.2015 / 03:40
0

Com zsh (pacote opcional no Solaris), usando algumas sintaxes somente de gravação:

$ for i (*/**/*(/)) echo ln -s -- $((${#${(s:/:)i}}-1))??depth.zip $i/${${i%/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]}:t}.zip
ln -s -- 1stdepth.zip a/2015-08-17/a.zip
ln -s -- 2nddepth.zip a/2015-08-17/a_b/a_b.zip
ln -s -- 3rddepth.zip a/2015-08-17/a_b/a_b_c/a_b_c.zip
ln -s -- 4thdepth.zip a/2015-08-17/a_b/a_b_c/a_b_c_1/a_b_c_1.zip
ln -s -- 4thdepth.zip a/2015-08-17/a_b/a_b_c/a_b_c_2/a_b_c_2.zip
ln -s -- 4thdepth.zip a/2015-08-17/a_b/a_b_c/a_b_c_3/a_b_c_3.zip

Remova o "eco" para realmente fazer isso.

Um pequeno detalhamento dos recursos específicos do zsh:

  • for f (...) cmd : forma abreviada do for i in ...; do ...; done . Apenas diferença sintática.
  • **/* : glob recursivo ( alguns outros shells copiaram isso desde )
  • (/) : qualificador glob: aqui, selecione apenas os arquivos do tipo diretório . Com outros shells, você pode fazer */ , embora isso também inclua links simbólicos para diretórios.
  • ${(s:/:)i} : sinalizadores de expansão variável. Aqui, s para dividir em / .
  • ${#${...}} : as expansões zsh geralmente podem ser aninhadas. Aqui ${#array} retorna o número de elementos na matriz, portanto, para a/b/c , 3.
  • ${var:t} : como em csh, retorne o tail (o nome da base).

Agora, isso é o que você pediu, mas provavelmente não é o que você deseja, o que cria links simbólicos quebrados. Você provavelmente iria querer tanto

ln -s -- /path/to/1stdepth.zip a/2015-08-17/a.zip

O que é facilmente resolvido adicionando $PWD :

for i (*/**/*(/)) echo ln -s -- $PWD/$((${#${(s:/:)i}}-1))??depth.zip $i/${${i%/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]}:t}.zip

Ou provavelmente melhor, um link relativo:

ln -s ../../1stdepth.zip a/2015-08-17/a.zip

O que você poderia fazer com:

setopt extendedglob
for i (*/**/*(/)) {
  z=($((${#${(s:/:)i}}-1))??depth.zip)
  echo ln -s -- ${i//[^\/]##/..}/$z $i/${${i%/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]}:t}.zip
}

Com ${i//[^\/]##/..} , substituímos sequências de caracteres não slash por .. .

(note que os -- não são necessários no Solaris, mas estariam nos sistemas GNU se houver diretórios cujo nome comece com - ).

    
por 08.10.2015 / 18:12