Avec un nom d'utilisateur passé à un script, recherchez le répertoire personnel de l'utilisateur

8

J'écris un script qui est appelé lorsqu'un utilisateur se connecte et vérifie si un certain dossier existe ou s'il s'agit d'un lien symbolique cassé. (C'est sur un système Mac OS X, mais la question est purement bash).

Ce n'est pas élégant et cela ne fonctionne pas, mais en ce moment, cela ressemble à ceci:

#!/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."

Le nœud du problème est que l'expansion du tilde ne fonctionne pas comme je le voudrais.

Disons que j'ai un utilisateur nommé georgeet que son dossier de départ est /a/path/to/georges_home. Si, sur un shell, je tape:

cd ~george

cela m'amène au répertoire approprié. Si je tape:

HOME_DIR=~george
echo $HOME_DIR

Ça me donne:

/a/path/to/georges_home

Cependant, si j'essaie d'utiliser une variable, cela ne fonctionne pas:

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

J'ai essayé d'utiliser des guillemets et des astuces, mais je n'arrive pas à comprendre comment le développer correctement. Comment faire pour que ça marche?


Addenda

Je voulais juste publier mon script terminé (vraiment, ce n'est pas aussi moche que le travail en cours ci-dessus!) Et dire qu'il semble fonctionner correctement.

#!/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  
Clinton Blackmore
la source

Réponses:

16

Utilisation $(eval echo ...):

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

Votre code devrait donc ressembler à:

HOMEDIR="$(eval echo ~$USERNAME)"
MikeyB
la source
1
+1 solution spécifique à votre script exact.
Warner
2

C'est parce que cela va enfermer le ~ george dans des guillemets simples lorsqu'il est défini comme une variable. set -xest utile pour le débogage.

Supprimez les guillemets lors de la définition DIRet le shell se développera lors de la définition de la variable, ce qui vous donnera les performances souhaitées.

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
Warner
la source
1
+1, j'étais loin sur celui-là :-)
Kyle Brandt
set -x est un bon conseil. Supprimer la citation de DIR n'a pas fonctionné pour moi sous bash 3.2.
Clinton Blackmore
GNU bash, version 2.05b.0 (1) -release (i486-slackware-linux-gnu) ici.
Warner
1

Bash a intégré des variables exportées pour le nom d'utilisateur et le répertoire personnel de l'utilisateur. Si vous appelez votre script lorsque l'utilisateur se connecte à partir de leur ~/.bash_profilepar exemple, vous n'aurez pas besoin de passer les valeurs comme arguments à votre script.

Vous pouvez utiliser $USERet $HOMEpuisqu'ils sont déjà définis et disponibles dans l'environnement de votre script car ils sont marqués comme exportés. Je pense que l'expansion du tilde est censée être plus pratique en ligne de commande que quelque chose qui est utilisé dans les scripts.

DIR="$HOME/Library/Caches"

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

Il peut être plus fiable d'obtenir le répertoire personnel de l'utilisateur de l'une des manières suivantes:

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

ou

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

En outre, exit 0indique le succès. D'une certaine manière, votre processus réussit à supprimer le répertoire, mais le fait qu'il doive être supprimé est une erreur en quelque sorte. Dans tous les cas, si vous exit 0à ce stade, vous ne pourrez pas faire la différence lorsque le script se terminera après la finale, echocar le code de sortie sera probablement nul.

En pause jusqu'à nouvel ordre.
la source
+1 Ce sont quelques-unes de mes difficultés, mais avec les variables HOME, je pense que cela est peut-être destiné à ne pas être exécuté en tant qu'UTILISATEUR, car il remplace la variable USERNAME avec le 3ème argument du script (ce qui me fait disparaître également). J'ai également tendance à penser que le tout ||et &&que le contrôle de flux (remplacer les instructions if) est un mauvais style.
Kyle Brandt
Le script sera exécuté via Casper. Je ne suis pas sûr qu'il regarde les codes de sortie, mais si c'est le cas, je veux toute instance où le script supprime le dossier de cache ou n'en trouve pas pour indiquer une exécution réussie du script, et toute instance où le le nom d'utilisateur n'a pas été fourni (indiquant que le script n'a pas été appelé lors de la connexion ou de la déconnexion) pour apparaître comme une erreur. Les erreurs sont signalées en rouge lorsque nous examinons les journaux de l'emplacement d'exécution du script.
Clinton Blackmore
@Clinton: Ensuite, vous voudrez avoir un nombre non nul pour la valeur de sortie (y compris un explicite exit nà la fin du script si l'échec est également une erreur. Zéro indique le succès et vous pouvez utiliser différents nombres non nuls pour indiquer différents types d'erreurs (c'est à vous de déterminer leur signification pour vos propres besoins). Si vous n'avez pas besoin de distinguer un type d'erreur d'un autre, il exit 1est aussi bon que n'importe quel à utiliser dans tous les cas pour indiquer un erreur générale.
pause jusqu'à nouvel ordre.
Je suppose qu'Unix est Unix et est tout de même ... sauf quand ce n'est pas le cas! Il semble que getent n'existe pas sur Mac et les utilisateurs normaux ne sont pas mentionnés dans / etc / passwd. La réponse de frère sur dscl suggère un moyen d'obtenir les mêmes informations sous OS X. J'apprécie votre réponse, et pourrait bien changer le script pour ne pas compter sur une évaluation paresseuse des opérateurs logiques.
Clinton Blackmore
1

À en juger par le chemin $HOME/Library/Caches, c'est Mac OS X, dsclvotre ami aussi.

Comme indiqué ci-dessus, le bashfait pour vous, et s'il s'agit d'un Mac, vous pouvez garantir bashqu'il sera disponible (et vous n'avez donc pas à vous soucier d'un strict conforme /bin/shne pouvant pas y faire face).

Mo.
la source
C'est un bon point. dscl /Search read /Users/$USERNAME NFSHomeDirectory | cut -f 2 -d " "semble être à peu près juste pour obtenir les informations que je recherchais.
Clinton Blackmore