Назад | Перейти на главную страницу

С помощью имени пользователя, переданного в сценарий, найдите домашний каталог пользователя.

Я пишу сценарий, который вызывается, когда пользователь входит в систему и проверяет, существует ли определенная папка или является ли символическая ссылка неработающей. (Это в системе Mac OS X, но вопрос чисто в bash).

Это не изящно и не работает, но сейчас это выглядит так:

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

Суть проблемы в том, что расширение тильды работает не так, как хотелось бы.

Допустим, у меня есть пользователь с именем george, и что его домашняя папка /a/path/to/georges_home. Если в оболочке я наберу:

cd ~george

он переводит меня в соответствующий каталог. Если я наберу:

HOME_DIR=~george
echo $HOME_DIR

Это дает мне:

/a/path/to/georges_home

Однако, если я попытаюсь использовать переменную, это не сработает:

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

Я пробовал использовать кавычки и обратные кавычки, но не могу понять, как заставить его правильно расширяться. Как мне заставить это работать?


Дополнение

Я просто хотел опубликовать свой завершенный сценарий (на самом деле, он не такой уродливый, как работа над ним выше!) И сказать, что он, похоже, работает правильно.

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

Использовать $(eval echo ...):

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

Итак, ваш код должен выглядеть так:

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

Это потому, что он будет заключать ~ george в одинарные кавычки при установке в качестве переменной. set -x полезен для отладки.

Убрать кавычки при установке DIR и оболочка будет расширяться при установке переменной, что даст вам желаемую производительность.

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

Bash имеет встроенные экспортируемые переменные для имени пользователя и домашнего каталога пользователя. Если вы вызываете свой скрипт, когда пользователь входит в систему со своего ~/.bash_profile например, вам не нужно будет передавать значения в качестве аргументов в ваш сценарий.

Ты можешь использовать $USER и $HOME поскольку они уже установлены и доступны в среде вашего скрипта, поскольку они помечены как экспортированные. Я думаю, что расширение тильды должно быть больше для удобства командной строки, чем для того, что используется в сценариях.

DIR="$HOME/Library/Caches"

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

Было бы более надежно получить домашний каталог пользователя одним из следующих способов:

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

или

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

Также, exit 0 указывает на успех. В каком-то смысле ваш процесс удаляет каталог, но тот факт, что его нужно удалить, является своего рода ошибкой. В любом случае, если вы exit 0 на этом этапе вы не сможете заметить разницу, когда скрипт завершится после последнего echo так как код выхода, скорее всего, будет нулевым.

Судя по пути $HOME/Library/Caches, это Mac OS X, поэтому dscl твой друг.

Как указано выше, bash сделает это за вас, и если это Mac, вы можете гарантировать bash будут доступны (и поэтому вам не нужно беспокоиться о строгом соответствии /bin/sh не в состоянии с этим справиться).