Com um nome de usuário passado para um script, encontre o diretório pessoal do usuário

8

Estou escrevendo um script que é chamado quando um usuário efetua login e verifica se existe uma determinada pasta ou se é um link simbólico quebrado. (Isso está em um sistema Mac OS X, mas a questão é puramente bash).

Não é elegante e não está funcionando, mas agora está assim:

#!/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 ponto crucial do problema é que a expansão do til não está funcionando como eu gostaria.

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

cd ~george

leva-me 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, isso não funcionará:

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

Eu tentei usar citações e backticks, mas não consigo descobrir como fazer isso se expandir adequadamente. Como faço isso funcionar?

Adendo

Eu só queria postar meu roteiro completo (na verdade, não é tão feio quanto o trabalho em andamento acima!) e dizer que parece estar funcionando direito.

#!/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  
    
por Clinton Blackmore 21.04.2010 / 18:21

4 respostas

16

Use $(eval echo ...) :

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

Assim, seu código deve se parecer com:

HOMEDIR="$(eval echo ~$USERNAME)"
    
por 21.04.2010 / 19:05
2

É porque ele irá incluir o george em singlequotes quando definido como uma variável. set -x é útil para depuração.

Remova as citações quando definir DIR e o shell expandirá ao definir a variável, o que lhe dará 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
    
por 21.04.2010 / 18:34
1

O Bash tem variáveis internas exportadas para o nome de usuário e o diretório pessoal do usuário. Se você estiver chamando seu script quando o usuário fizer login em ~/.bash_profile , por exemplo, você não precisará passar os valores como argumentos para seu script.

Você pode usar $USER e $HOME , pois eles já estão definidos e disponíveis no ambiente do script, pois estão marcados como exportados. Acho que a expansão do til deve ser mais uma conveniência de 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 pessoal do usuário de uma das seguintes formas:

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 0 indica sucesso. De certa forma, seu processo é bem-sucedido ao excluir o diretório, mas o fato de precisar ser excluído é um tipo de erro. De qualquer forma, se você exit 0 neste ponto não conseguirá dizer a diferença quando o script sair depois do echo final, já que o código de saída provavelmente será zero.

    
por 21.04.2010 / 23:29
1

A julgar pelo caminho $HOME/Library/Caches , este é o Mac OS X, então dscl é seu amigo.

Como mencionado acima, bash faz isso para você, e se for um Mac, você pode garantir que bash estará disponível (e, portanto, não precisa se preocupar com um /bin/sh estritamente em conformidade) não ser capaz de lidar com isso).

    
por 21.04.2010 / 23:37

Tags