Con un nombre de usuario pasado a un script, busque el directorio de inicio del usuario

8

Estoy escribiendo un script que recibe una llamada cuando un usuario inicia sesión y comprueba si existe una carpeta determinada o si hay un enlace simbólico roto. (Esto está en un sistema Mac OS X, pero la pregunta es puramente bash).

No es elegante y no funciona, pero en este momento se ve así:

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

El quid del problema es que la expansión de tilde no funciona como me gustaría.

Digamos que tengo un usuario llamado georgey que su carpeta de inicio es /a/path/to/georges_home. Si, en un shell, escribo:

cd ~george

Me lleva al directorio apropiado. Si escribo:

HOME_DIR=~george
echo $HOME_DIR

Me da:

/a/path/to/georges_home

Sin embargo, si trato de usar una variable, no funciona:

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

He intentado usar comillas y comillas, pero no puedo encontrar la manera de expandirlo correctamente. ¿Cómo hago para que esto funcione?


Apéndice

Solo quería publicar mi guión completo (en realidad, ¡no es tan feo como el trabajo en progreso anterior!) Y decir que parece estar funcionando bien.

#!/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
fuente

Respuestas:

16

Uso $(eval echo ...):

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

Entonces su código debería verse así:

HOMEDIR="$(eval echo ~$USERNAME)"
MikeyB
fuente
1
Solución +1 específica para su script exacto.
Warner
2

Es porque va a encerrar el ~ george dentro de comillas simples cuando se establece como una variable. set -xEs útil para la depuración.

Elimine las comillas al configurar DIRy el shell se expandirá al configurar la variable, lo que le dará el rendimiento deseado.

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
fuente
1
+1, estaba lejos de eso :-)
Kyle Brandt
set -x es un buen consejo. Eliminar la cita de DIR no funcionó para mí en bash 3.2.
Clinton Blackmore
GNU bash, versión 2.05b.0 (1) -release (i486-slackware-linux-gnu) aquí.
Warner
1

Bash tiene variables exportadas incorporadas para el nombre de usuario y el directorio de inicio del usuario. Si está llamando a su secuencia de comandos cuando el usuario inicia sesión desde su, ~/.bash_profilepor ejemplo, no necesitará pasar los valores como argumentos a su secuencia de comandos.

Puede usar $USERy $HOMEdado que ya están configurados y disponibles en el entorno de su secuencia de comandos, ya que están marcados como exportados. Creo que la expansión tilde está destinada a ser más una conveniencia de línea de comandos que algo que se usa en los scripts.

DIR="$HOME/Library/Caches"

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

Puede ser más confiable obtener el directorio de inicio del usuario de una de las siguientes maneras:

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

o

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

Además, exit 0indica éxito. En cierto modo, su proceso tiene éxito al eliminar el directorio, pero el hecho de que necesite eliminarse es un error de algún tipo. En cualquier caso, si exit 0en este momento no podrá notar la diferencia cuando la secuencia de comandos sale después de la final echoya que el código de salida probablemente será cero.

Pausado hasta nuevo aviso.
fuente
+1 Esos fueron algunos de mis puntos fuertes, pero con las variables HOME creo que esto no debe ejecutarse como USUARIO, ya que anula la variable USERNAME con el tercer argumento del guión (que también me quitó). También tiendo a pensar que todo ||y &&como control de flujo (reemplazar las declaraciones if) es un mal estilo.
Kyle Brandt
El script se ejecutará a través de Casper. No estoy seguro de si mira los códigos de salida, pero si lo hace, quiero cualquier instancia donde el script elimine la carpeta de caché o no encuentre uno para indicar una ejecución exitosa del script, y cualquier instancia donde el script no se proporcionó el nombre de usuario (lo que indica que no se llamó al script al iniciar sesión o al cerrar sesión) para que aparezca como un error. Los errores se marcan en rojo cuando miramos los registros de donde se ejecutó el script.
Clinton Blackmore
@Clinton: Entonces querrá tener un número distinto de cero para el valor de salida (incluido un explícito exit nal final del guión si el incumplimiento también es un error. Cero indica éxito y puede usar diferentes números distintos de cero para indique diferentes tipos de errores (depende de usted determinar su significado para sus propios fines). Si no necesita distinguir un tipo de error de otro, entonces exit 1es tan bueno como cualquiera para usar en todos los casos para indicar un error general.
Pausado hasta nuevo aviso.
Supongo que unix es unix y es lo mismo ... ¡excepto cuando no lo es! Parece que getent no existe en Mac, y los usuarios normales no se mencionan en / etc / passwd. La respuesta entre hermanos en dscl sugiere una forma de obtener la misma información en OS X. Agradezco su respuesta, y bien podría cambiar el script para no confiar en la evaluación perezosa de los operadores lógicos.
Clinton Blackmore
1

A juzgar por el camino $HOME/Library/Caches, este es Mac OS X, también lo dscles tu amigo.

bashSin embargo , como se señaló anteriormente, lo hace por usted, y si se trata de una Mac, puede garantizar bashque estará disponible (y, por lo tanto, no debe preocuparse por una estricta conformidad que /bin/shno pueda hacer frente a ella).

Mes.
fuente
Ese es un buen punto. dscl /Search read /Users/$USERNAME NFSHomeDirectory | cut -f 2 -d " "parece ser lo correcto para obtener la información que buscaba.
Clinton Blackmore