Como verificar se $ PWD é um subdiretório de um determinado caminho

25

Por exemplo, verifique se $PWDé um subdiretório de / home. Em outras palavras, estou procurando por uma operação de seqüência de caracteres bash para verificar se uma sequência começa com outra.

Tobias Kienzler
fonte

Respostas:

26

Se você deseja testar com segurança se um diretório é um subdiretório de outro, precisará de mais do que apenas uma verificação de prefixo de sequência. A resposta de Gilles descreve em detalhes como fazer esse teste corretamente.

Mas se você deseja uma verificação simples do prefixo de sequência (talvez você já tenha normalizado seus caminhos?), Esta é uma boa:

test "${PWD##/home/}" != "${PWD}"

Se $PWDcomeçar com "/ home /", ele será retirado do lado esquerdo, o que significa que não corresponderá ao lado direito, então "! =" Retornará verdadeiro.

Jander
fonte
11
+1 perfeito, e para um caso mais geral: [ "${PWD##$VAR}" != "$PWD" ]e se essa fosse uma pergunta [code-golf] ( stackoverflow.com/questions/tagged/code-golf)), você teria me vencido em dois caracteres ;-) Também parece mais legível ...
Tobias Kienzler 26/01
FWIW, isso também pode ser útil:${PWD/#$HOME/\~}
user1338062
Tudo bem, mas que tal PWD = "/ home /../ etc /" #
Alexis Peters #
11
@ AlexisPeters: Você está certo: eu estava focando na parte "prefixo de string" da pergunta e editei para deixar isso mais claro. Gilles cobriu bem a maneira correta de verificar subdiretórios.
Jander
11
Use "${PWD%%/subdir*}"para detectar se o usuário está atualmente em subdirou em um subdiretório de subdir, pois %% captura do final da sequência em vez do início. Isso deve ser útil para qualquer pessoa à procura de mais informações sobre a substituição de parâmetros: tldp.org/LDP/abs/html/parameter-substitution.html
George Pantazes
33

Para testar se uma string é um prefixo de outra, em qualquer shell no estilo Bourne:

case $PWD/ in
  /home/*) echo "home sweet home";;
  *) echo "away from home";;
esac

O mesmo princípio funciona para um teste de sufixo ou substring. Observe que nas caseconstruções, diferente dos nomes dos arquivos, *corresponde a qualquer caractere, incluindo um /ou uma inicial ..

Nos shells que implementam a [[ … ]]sintaxe (ou seja, bash, ksh e zsh), ele pode ser usado para combinar uma string com um padrão. (Observe que o [comando só pode testar cadeias de caracteres para igualdade.)

if [[ $PWD/ = /home/* ]]; then 

Se você estiver testando especificamente se o diretório atual está abaixo /home, um simples teste de substring não será suficiente, devido a links simbólicos.

Se /homefor um sistema de arquivos próprio, teste se o diretório atual ( .) está nesse sistema de arquivos.

if [ "$(df -P . | awk 'NR==2 {print $6}')" = "/home" ]; then
  echo 'The current directory is on the /home filesystem'
fi

Se você possui o NetBSD, OpenBSD ou GNU (ou seja, Linux) readlink, pode usar readlink -fpara remover links simbólicos de um caminho.

case $(readlink -f .)/ in $(readlink -f /home)/*) 

Caso contrário, você pode usar pwdpara mostrar o diretório atual. Mas você deve tomar cuidado para não usar um shell interno se o seu shell rastrear cdcomandos e manter o nome que você usou para acessar o diretório em vez de sua localização "real".

case $(pwd -P 2>/dev/null || env PWD= pwd)/ in
  "$(cd /home && { pwd -P 2>/dev/null || env PWD= pwd; })"/*) 
Gilles 'SO- parar de ser mau'
fonte
+1 Obrigado por esta resposta exaustiva. Eu não considero links e peculiaridades do sistema de arquivos quando perguntando isso, mas é definitivamente bom saber
Tobias KIENZLER
8

Versão bruta:

[ ${PWD:0:6} = "/home/" ]

Tem a desvantagem de contar primeiro os caracteres e não se pode substituir /home/por algo geral $1.

editar (obrigado @ Michael) pela generalização para comparar com $VARum pode usar

[ "${PWD:0:${#VAR}}" = $VAR ]
Tobias Kienzler
fonte
4
${#var}é o comprimento de $var, para que você possa generalizá-lo como[ "${PWD:0:${#1}}" = "$1" ]
Michael Mrozek
@ Michael Mrozek ♦: Obrigado, funciona perfeito :)
Tobias KIENZLER
2

Eu não entendo a pergunta muito bem, mas para encontrar o pai de $ PWD , faça dirname $PWD. Para encontrar o pai do pai, execute dirname $(dirname $PWD)e assim por diante ...

tshepang
fonte
Isso só funcionaria se eu soubesse a profundidade, caso contrário, acabarei com /. Ok, eu poderia verificar recursivamente, mas não há algo mais fácil?
Tobias Kienzler
11
@tob Se eu soubesse programação shell ;-)
tshepang
1

Usando awk:

echo $PWD | awk -v h="/home" '$0 ~ h {print "MATCH"}'
dogbane
fonte
+1 para trabalhar, mas um pouco mais lento do que um teste de festa nativa
Tobias KIENZLER
0

Hum, é uma pena que [não tenha uma opção de testar a STRING1 starts with STRING2condição.

Você pode tentar echo $PWD | grep '^$VAR', mas pode falhar de maneiras interessantes quando VARcontém símbolos especiais.

awkA indexfunção de deve poder executar o truque. Mas tudo isso parece pesado demais para uma coisa tão fácil de testar.

alex
fonte
[[regexp para que você comece com [[$ PWD = ~ ^ \ / home \ /]]
teknopaul 2/15/15
0

Se a parte pesquisada do caminho for encontrada, eu "esvazio" a variável:

[[ -z "${PWD//*\/home\/*/}" ]] && echo "toto"

fonte