Decomposição das especificações de caminho no prefixo + longo mais comum + sufixo

4

Se forem fornecidas duas especificações absolutas do caminho Unix 1 , pode-se decompor cada especificação como a concatenação de um prefixo comum mais longo e um sufixo específico. Por exemplo,

/abc/bcd/cdf     -> /abc/bcd + cdf
/abc/bcd/chi/hij -> /abc/bcd + chi/hij

Existe um utilitário Unix (ou utilitários) para calcular essa decomposição? (Eu adicionei "ou utilitários" no caso de haver utilitários separados para calcular o prefixo comum mais longo e para calcular caminhos relativos).

(Eu percebo que não seria extremamente difícil codificar tais utilitários, mas eu tento dar prioridade a ferramentas que são mais ou menos padronizadas em relação às customizadas, sempre que possível.)

1 Eu escrevo "path spec" em vez de "path" para evitar problemas como existência (dos caminhos) em um determinado sistema de arquivos, links, etc. p>     

por kjo 06.03.2013 / 19:01

4 respostas

1

Você pode fazer isso em um loop de shell. O código abaixo deve funcionar com todos os tipos de caminhos estranhos com barras extras; se todos os seus caminhos estiverem no formato /foo/bar , você poderá usar algo mais simples.

split_common_prefix () {
  path1=$1
  path2=$2
  common_prefix=
  ## Handle initial // specially
  case $path1 in
    //[!/]*) case $path2 in
               //[!/]*) common_prefix=/ path1=${path1#/} path2=${path2#/};;
               *) return;;
             esac;;
    /*) case $path2 in
          /*) :;;
          *) return;;
        esac;;
    *) case $path2 in /*) return;; esac;;
  esac
  ## Normalize multiple slashes
  trailing_slash1= trailing_slash2=
  case $path1 in */) trailing_slash1=/;; esac
  case $path2 in */) trailing_slash2=/;; esac
  path1=$(printf %s/ "$path1" | tr -s / /)
  path2=$(printf %s/ "$path2" | tr -s / /)
  if [ -z "$trailing_slash1" ]; then path1=${path1%/}; fi
  if [ -z "$trailing_slash2" ]; then path2=${path2%/}; fi
  ## Handle the complete prefix case (faster, necessary for equality and
  ## for some cases with trailing slashes)
  case $path1 in
    "$path2")
      common_prefix=$path1; path1= path2=
      return;;
    "$path2"/*)
      common_prefix=$path2; path1=${path1#$common_prefix} path2=
      return;;
  esac
  case $path2 in
    "$path1"/*)
      common_prefix=$path1; path1= path2=${path2#$common_prefix}
      return;;
  esac
  ## Handle the generic case
  while prefix1=${path1%%/*} prefix2=${path2%%/*}
        [ "$prefix1" = "$prefix2" ]
  do
    common_prefix=$common_prefix$prefix1/
    path1=${path1#$prefix1/} path2=${path2#$prefix1/}
  done
}

Como alternativa, determine o prefixo comum mais longo das duas strings e corte-o em seu último caractere / (exceto quando o prefixo comum consiste apenas em barras).

    
por 07.03.2013 / 02:26
8

Você pode calcular a subseqüência de substring comum mais longa de uma lista de linhas com isso:

sed -e '1{h;d;}' -e 'G;s,\(.*\).*\n.*,,;h;$!d'

Que por exemplo para:

/abc/bcd/cdf
/abc/bcd/cdf/foo
/abc/bcd/chi/hij
/abc/bcd/cdd

retorna:

/abc/bcd/c

Para restringir os componentes do caminho:

sed -e 's,$,/,;1{h;d;}' -e 'G;s,\(.*/\).*\n.*,,;h;$!d;s,/$,,'

(retorna /abc/bcd na amostra acima).

    
por 07.03.2013 / 01:00
2

Não existe essa ferramenta para o meu conhecimento. No entanto, você pode escrever facilmente esse programa, já que precisa determinar o maior grupo de componentes.

Um exemplo "one-liner":

echo /abc/bcd/cdf | awk -vpath=/abc/bcd/chi/hij -F/ '{ OFS="\n";len=0; split(path, components); for (i=1; i<=NF; i++) if($i == components[i])len+=1+length($i);else break;print substr($0, 1, len - 1), substr($0, len + 1), substr(path, len + 1);exit;}

Versão formatada com comentários:

$ cat longest-path.awk
#!/usr/bin/awk -f
BEGIN {
    FS="/";   # split by slash
}
{
    len=0;                      # initially the longest path has length 1
    split(path, components);    # split by directory separator (slash)
    for (i=1; i<=NF; i++) {     # loop through all path components
        if ($i == components[i]) {
            len += 1 + length($i);
        } else {
            break;              # if there is a mismatch, terminate
        }
    }
    print substr($0, 1, len - 1);  # longest prefix minus slash
    print substr($0, len + 1);     # remainder stdin
    print substr(path, len + 1);   # remainder path
    exit;                          # only the first line is compared
}
$ echo  /abc/bcd/cdf | ./longest-path.awk -vpath=/abc/bcd/chi/hij
/abc/bcd
cdf
chi/hij
    
por 06.03.2013 / 19:46
0

Aqui está uma rapidinha que parece responder à pergunta, fazendo bom uso dos recursos usuais (padrão?) do unix / linux, conforme solicitado (bem ... eu tentei apenas no meu Mageia Linux).

#!/bin/sh
# Compute absolute pathnames common prefix and decompose second one
# Author Babou 2013/05/27 on http://unix.stackexchange.com/questions/67078/
first='realpath -ms "$1"'
rel='realpath -ms --relative-to="$1" "$2" | rev'
while [ 'basename "$rel"' == '..' ]
do
   first='dirname "$first"'
   rel='dirname "$rel"'
done
echo $first + 'echo $rel | rev'

E minha suíte de testes:

./prefix /abc/bcd/cdf /abc/bcd/chi/hij
./prefix "/abc/bcd/cdf" "/abc/bcd/chi/hij"
./prefix "/ab c/bcd/cdf" "/ab c/bcd/chi/hij"
./prefix "/abc/bcd/cdf" "/abc/bcd/chi/h ij"
./prefix "/" "/"
./prefix "/abc/bcd/" "/abc/bcd/chi/hij"
./prefix "/abc/bcd/cdf" "/abc/bcd/"
./prefix "/abc///zzz/../bcd/cdf" "///abc/bcd//chi/h i j/"
./prefix "/abèc/bcd/cdf" "/abèc/bcd/"

dois exemplos:

$ ./prefix "/abc///zzz/../bcd/cdf" "///abc/bcd//chi/h i j/"
/abc/bcd + chi/h i j
$ ./prefix "/abèc/bcd/cdf" "/abèc/bcd/"
/abèc/bcd + .

Se você deseja decompor os dois caminhos, você pode modificar o script ou aplicá-lo duas vezes, alterando a ordem ou os argumentos.

Eu não estou muito feliz com nomes de variáveis ... mas minha primeira nota ruim em programação foi devido a uma falha de conversão alfa (uma ocorrência esquecida). Então deixo como está.

P.S. Você pode querer unificar a apresentação do caminho relativo (segunda parte da decomposição) quando vazia: pode vir como "." ou como "/" em um caso, quando os dois caminhos são apenas "/".

    
por 27.05.2013 / 15:31