você pode fazer isso como:
cd /usr///.//share/../share//man/man1 || exit
IFS=/; set -f
printf %.1s/ ${PWD%/*}
printf %s\\n "${PWD##*/}"
/u/s/m/man1
e aqui está um sed
:
printf %s "$file" |
tr /\\n \\n/ | sed -et$ \
-e '\|^\.\.$|{x;s|\(.*\)\n.*$|\1|;x;}' \
-e 's|^\.\{0,2\}$||;\|.|H;$!d;x' \
-e$ -e '\|\(\.\{0,2\}.\)\(.*\)\(\n\)|!b' \
-e 's||\1\3\2\3|;P;s|\n||;D' |
tr /\\n \\n/
que chega bem perto de fazer as mesmas coisas que a função faz abaixo. ele não abrevia com tildes ou insere a $PWD
cabeça de uma não barra invertida como a função (e de fato, nunca imprime a barra invertida), mas isso pode ser tratado posteriormente. ele processa componentes de caminho nulo, pontos únicos e elimina ..
casos.
dado o mesmo man
caminho cd
acima, ele imprime:
u/s/m/man1
também imprimirá um ou dois pontos iniciais extras para cada componente do caminho que começa com esse e não é apenas um ou dois pontos.
você perguntou sobre fazer mais do que o caractere para um componente de caminho que começa com a .
. para fazer isso, imaginei que cada componente precisaria de atenção individual de qualquer maneira e, como estava curioso, tentei trabalhar um caminho canônico sem o diretório de alterações. depois de alguma tentativa e erro, decidi que a única maneira de fazer isso direito era fazê-lo duas vezes - para trás e para frente:
pathbytes(){
local IFS=/ o="$-" p
set -f${ZSH_VERSION+LFy}
set -- ${1:-$PWD}
for p in /${1:+$PWD} $*
do case $p in (.|"") ;;
(..) ${1+shift} ;;
(/) set -- ;;
(*) set -- $p $*; esac
done
for p in //$* ""
do case ${p:-/$3} in
([!./]*) ;;
(..*) set "..$@" ;;
(.*) set ".$@" ;;
(//*) ! set "" $1 $1 ;;
(~) ! p=\~ ;;
(~/*) p="~/$2";set $HOME
! while "${2+shift}" 2>&3
do p="~/${p#??*/}"
done 3>/dev/null;;
esac&& set "" "${p%"${p#$1?}"}/$2" "$p/$3"
done; printf %s\\n "${p:-$2}"
set +f "-${o:--}"
}
para que nunca mude o diretório ou tente confirmar a existência de qualquer componente do caminho, mas aperta /
delimitadores repetidos e elimina /./
completamente os componentes de ponto único e processa /../
os componentes de ponto duplo adequadamente.
Quando $IFS
é definido como um caractere que não é um espaço em branco , uma sequência de dois ou mais $IFS
caracteres resultará em um ou mais campos nulos. para que várias barras consecutivas funcionem com argumentos de valor nulo. o mesmo vale para um $IFS
personagem principal . e assim, quando se set -- $1
divide, se o resultado $1
for nulo, ele começará com uma barra, caso contrário, ${1:+$PWD}
se não for nulo, insiro $PWD
. em outras palavras, se o primeiro argumento não começar com uma barra, ele será $PWD
anexado. é o mais próximo possível da validação de caminho .
caso contrário, o primeiro for
loop inverte recursivamente a ordem dos componentes do caminho, como:
1 2 3
1 2 3
2 1 3
3 2 1
... ao fazer isso, ignora qualquer componente de ponto único ou nulo, e por ..
isso ...
1 .. 3
1 .. 3
3
3
... a segunda passagem inverte esse efeito e, ao fazê-lo, comprime cada componente em 2 pontos + caractere ou 1 ponto + caractere ou caractere .
portanto, deve seguir um caminho canônico, independentemente da existência.
eu adicionei / subtraí um pouco ao segundo loop. agora set
é menos frequente (apenas uma vez para cada [!./]*
componente) e case
avaliações de padrão de curto-circuito na maioria das vezes (graças ao padrão mencionado acima) e inclui uma avaliação de correspondência de chamada de cauda contra ~
. se todo ou uma parte inicial (dividida em componentes inteiros) do caminho finalmente canônico puder corresponder ~
, o bit correspondente será retirado e um literal ~
será substituído. para fazer isso, eu tive que manter uma cópia completa do caminho ao lado do abreviado também (porque combinar o caminho abreviado ~
provavelmente não seria muito útil) , e isso é mantido $3
. o últimowhile
A ramificação de loop é executada apenas se ~
corresponder a um subconjunto de $3
.
se você executá-lo com o set -x
rastreamento ativado, poderá vê-lo funcionar.
$ (set -x;pathbytes ..abc/def/123///././//.././../.xzy/mno)
+ pathbytes ..abc/def/123///././//.././../.xzy/mno
+ local IFS=/ o=xsmi p
+ set -f
+ set -- ..abc def 123 . . .. . .. .xzy mno
+ set --
+ set -- home
+ set -- mikeserv home
+ set -- ..abc mikeserv home
+ set -- def ..abc mikeserv home
+ set -- 123 def ..abc mikeserv home
+ shift
+ shift
+ set -- .xzy ..abc mikeserv home
+ set -- mno .xzy ..abc mikeserv home
+ set mno mno
+ set . mno mno
+ set .x/mno .xzy/mno
+ set .. .x/mno .xzy/mno
+ set ..a/.x/mno ..abc/.xzy/mno
+ set m/..a/.x/mno mikeserv/..abc/.xzy/mno
+ set h/m/..a/.x/mno home/mikeserv/..abc/.xzy/mno
+ p=~/h/m/..a/.x/mno
+ set home mikeserv
+ shift
+ p=~/m/..a/.x/mno
+ shift
+ p=~/..a/.x/mno
+
+ printf %s\n ~/..a/.x/mno
~/..a/.x/mno
+ set +f -xsmi
/f/b/.c/wizard_magic
. O ponto geralmente é tão comum em um diretório específico que é uma pista muito pequena de onde você deve procurar.