Com um nome de usuário passado para um script, localize o diretório inicial do usuário


8

Estou escrevendo um script chamado quando um usuário efetua login e verifica se existe uma determinada pasta ou se há um link simbólico quebrado. (Isso ocorre em um sistema Mac OS X, mas a pergunta é puramente básica).

Não é elegante e não está funcionando, mas agora parece com isso:

#!/bin/bash

# Often users have a messed up cache folder -- one that was redirected
# but now is just a broken symlink.  This script checks to see if
# the cache folder is all right, and if not, deletes it
# so that the system can recreate it.

USERNAME=$3
if [ "$USERNAME" == "" ] ; then
    echo "This script must be run at login!" >&2
    exit 1
fi

DIR="~$USERNAME/Library/Caches"

cd $DIR || rm $DIR && echo "Removed misdirected Cache folder" && exit 0

echo "Cache folder was fine."

O cerne do problema é que a expansão til não está funcionando como eu gostaria.

Digamos que eu tenha um usuário chamado georgee que seja sua pasta pessoal /a/path/to/georges_home. Se, em um shell, eu digitar:

cd ~george

isso me leva ao diretório apropriado. Se eu digitar:

HOME_DIR=~george
echo $HOME_DIR

Isso me dá:

/a/path/to/georges_home

No entanto, se eu tentar usar uma variável, ela não funcionará:

USERNAME="george"
cd ~$USERNAME
-bash: cd: ~george: No such file or directory

Tentei usar aspas e backticks, mas não consigo descobrir como fazê-lo expandir adequadamente. Como eu faço isso funcionar?


Termo aditivo

Eu só queria postar meu script completo (realmente, não é tão feio quanto o trabalho em andamento acima!) E dizer que ele parece estar funcionando corretamente.

#!/bin/bash

# Often users have a messed up cache folder -- one that was redirected
# but now is just a broken symlink.  This script checks to see if
# the cache folder is all right, and if not, deletes it
# so that the system can recreate it.

#set -x # turn on to help debug

USERNAME=$3 # Casper passes the user name as parameter 3
if [ "$USERNAME" == "" ] ; then
    echo "This script must be run at login!" >&2
    exit 1  # bail out, indicating failure
fi

CACHEDIR=`echo $(eval echo ~$USERNAME/Library/Caches)`

# Show what we've got
ls -ldF "$CACHEDIR"

if [ -d "$CACHEDIR" ] ; then
    # The cache folder either exists or is a working symlink
    # It doesn't really matter, but might as well output a message stating which
    if [ -L "$CACHEDIR" ] ; then
        echo "Working symlink found at $CACHEDIR was not removed."
    else
        echo "Normal directory found at $CACHEDIR was left untouched."
    fi
else
    # We almost certainly have a broken symlink instead of the directory
    if [ -L "$CACHEDIR" ] ; then
        echo "Removing broken symlink at $CACHEDIR."
        rm "$CACHEDIR"
    else
        echo "Abnormality found at $CACHEDIR.  Trying to remove." >&2
        rm -rf "$CACHEDIR"
        exit 2  # mark this as a bad attempt to fix things; it isn't clear if the fix worked
    fi
fi

# exit, indicating that the script ran successfully,
# and that the Cache folder is (almost certainly) now in a good state
exit 0  

Respostas:


16

Use $(eval echo ...):

michael:~> USERNAME=michael
michael:~> echo ~michael
/home/michael
michael:~> echo ~$USERNAME
~michael
michael:~> echo $(eval echo ~$USERNAME)
/home/michael

Portanto, seu código deve se parecer com:

HOMEDIR="$(eval echo ~$USERNAME)"

1
Solução +1 específica para seu script exato.
21410 Warner

2

É porque ele irá colocar o ~ george entre aspas simples quando definido como uma variável. set -xé útil para depuração.

Remova a citação ao definir DIRe o shell se expandirá ao definir a variável, o que fornecerá o desempenho desejado.

lrwxrwxrwx  1 root root 4 Sep 10  2004 /bin/sh -> bash*
wmoore@bitbucket(/tmp)$ cat test.sh
#!/bin/bash
set -x

cd ~root

DIR=~root

cd $DIR

DIR="~root"

cd $DIR
wmoore@bitbucket(/tmp)$ sh test.sh
+ cd /root
test.sh: line 4: cd: /root: Permission denied
+ DIR=/root
+ cd /root
test.sh: line 8: cd: /root: Permission denied
+ DIR=~root
+ cd '~root'
test.sh: line 12: cd: ~root: No such file or directory

1
+1, eu estava longe de que um :-)
Kyle Brandt

set -x é uma boa dica. Remover a citação do DIR não funcionou para mim no bash 3.2.
Clinton Blackmore

GNU bash, versão 2.05b.0 (1) -release (i486-slackware-linux-gnu) aqui.
22410 Warner

1

O Bash incorporou variáveis ​​exportadas para o nome de usuário e o diretório inicial do usuário. Se você estiver chamando seu script quando o usuário fizer login, ~/.bash_profilepor exemplo, não será necessário passar os valores como argumentos para o seu script.

Você pode usar $USERe, $HOMEjá que eles já estão definidos e disponíveis no ambiente do seu script, pois estão marcados como exportados. Eu acho que a expansão til deve ser mais uma conveniência da linha de comando do que algo usado em scripts.

DIR="$HOME/Library/Caches"

cd "$DIR" || rm "$DIR" && echo "Removed misdirected Cache folder" && exit 1

Pode ser mais confiável obter o diretório inicial do usuário de uma das seguintes maneiras:

getent passwd $USER | awk -F: '{print $(NF - 1)}'

ou

awk -F: -v user=$USER 'user == $1 {print $(NF - 1)}' /etc/passwd

Além disso, exit 0indica sucesso. De certa forma, seu processo foi bem-sucedido ao excluir o diretório, mas o fato de ele precisar ser excluído é um erro de uma classificação. De qualquer forma, se você exit 0não apontar a diferença quando o script sair após a final, echojá que o código de saída provavelmente será zero.


+1 Esses foram alguns dos meus pontos fortes, mas com as variáveis ​​HOME acho que talvez isso não deva ser executado como USUÁRIO, pois ele substitui a variável USERNAME pelo terceiro argumento do script (o que também ocorre comigo). Eu também tendem a pensar que o todo ||e &&como o controle de fluxo (substituindo as instruções if) são ruins.
Kyle Brandt

O script será executado através do Casper. Não tenho certeza se ele procura os códigos de saída, mas, se o fizer, desejo qualquer instância em que o script exclua a pasta de cache ou não encontre uma que indique uma execução bem-sucedida do script e qualquer instância em que o o nome de usuário não foi fornecido (indicando que o script não foi chamado no login ou logout) para aparecer como um erro. Os erros são sinalizados em vermelho quando olhamos para os logs de onde o script foi executado.
Clinton Blackmore

@Clinton: Então você deseja ter um número diferente de zero para o valor de saída (incluindo um explícito exit nno final do script, se cair também for um erro. Zero indica sucesso e você pode usar números diferentes de zero para indicar tipos diferentes de erros (depende de você determinar o significado deles para seus próprios propósitos) .Se você não precisar distinguir um tipo de erro de outro, exit 1será tão bom quanto qualquer outro usar em todos os casos para indicar um erro. erro geral ..
Pausado até novo aviso.

Eu acho que unix é unix e é tudo a mesma coisa ... exceto quando não é! Parece que o getent não existe no Mac e os usuários normais não são mencionados em / etc / passwd. A resposta do irmão no dscl sugere uma maneira de obter as mesmas informações no OS X. Agradeço sua resposta e é possível que mude o script para não confiar na avaliação preguiçosa dos operadores lógicos.
Clinton Blackmore

1

A julgar pelo caminho $HOME/Library/Caches, este é o Mac OS X, assim dsclcomo seu amigo.

Como mencionado acima, você bashfaz isso para você e, se for um Mac, você pode garantir bashque estará disponível (e assim você não precisa se preocupar com o fato de /bin/shnão conseguir lidar com isso).


Este é um bom ponto. dscl /Search read /Users/$USERNAME NFSHomeDirectory | cut -f 2 -d " "parece estar certo para obter as informações que eu estava procurando.
Clinton Blackmore
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.