você pode fazer assim:
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.*$||;x;}' \
-e 's|^\.\{0,2\}$||;\|.|H;$!d;x' \
-e$ -e '\|\(\.\{0,2\}.\)\(.*\)\(\n\)|!b' \
-e 's|||;P;s|\n||;D' |
tr /\n \n/
que chega perto de fazer as mesmas coisas que a função faz abaixo. ele não abrevia com tils ou insere o $PWD
na cabeça para uma não-barra líder como a função faz (e, na verdade, nunca imprime a barra inicial) mas que poderia ser manipulada posteriormente . ele processa componentes de caminho nulo e pontos únicos e elimina ..
casos.
considerando o mesmo caminho man
, como cd
acima, imprime:
u/s/m/man1
ele também imprime um ou dois pontos iniciais extras para cada componente do caminho que começa com tal e não é apenas um ou dois pontos.
você perguntou sobre fazer mais do que um caractere para um componente de caminho que começa com .
. para fazer isso eu percebi que cada componente precisaria de atenção individual de qualquer maneira, e porque eu estava curioso, eu tentei a minha mão trabalhando em um caminho canônico sem o diretório de mudança. Depois de algumas tentativas e erros, decidi que a única maneira de fazer o certo 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 de caminho, mas ele comprime os componentes /
e gotas /./
single-dot repetidos e processa os componentes /../
double-dot apropriadamente.
quando $IFS
é definido para algum caractere não espaço em branco , uma sequência de dois ou mais caracteres $IFS
resultará em um ou mais campos nulos. Assim, várias barras consecutivas funcionam com argumentos de valor nulo. o mesmo é verdadeiro para um caractere $IFS
inicial. e assim quando set -- $1
divide, se o $1
resultante for nulo, então começou com uma barra, senão, ${1:+$PWD}
se não for nulo, então eu insiro $PWD
. Em outras palavras, se o primeiro argumento não iniciar com uma barra, ele receberá $PWD
prefixado. isso é o mais próximo que isso chega ao caminho validação .
caso contrário, o primeiro loop for
recursivamente inverte a ordem dos componentes do caminho, como:
1 2 3
1 2 3
2 1 3
3 2 1
... enquanto faz isso, ele ignora qualquer componente de ponto único ou nulo, e para ..
ele faz ...
1 .. 3
1 .. 3
3
3
... o segundo passo reverte esse efeito e, ao fazer isso, ele pressiona cada componente para 2-pontos + char , ou 1-ponto + char , ou char .
por isso deve funcionar para um caminho canônico, independentemente da existência.
eu adicionei / subtrai um pouco ao segundo loop. agora set
s menos frequentemente (apenas uma vez para cada [!./]*
componente) , e curtos circuitos case
avaliações de padrões na maioria das vezes (graças ao padrão mencionado acima) , e inclui uma avaliação de correspondência da última chamada em relação a ~
. se all ou uma parte inicial (dividida em componentes inteiros) do caminho canônico final puder corresponder a ~
, o bit correspondente será removido e um literal ~
será substituído. A fim de fazer isso, eu tive que manter uma cópia completa do caminho ao lado do abreviado, bem (porque a correspondência do caminho abreviado para ~
provavelmente não seria muito útil) , e assim é mantido em $3
. a última ramificação de loop while
só será executada se ~
for correspondido como um subconjunto de $3
.
se você executá-lo com o set -x
trace ativado, poderá assisti-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