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

8

Я пишу скрипт, который вызывается, когда пользователь входит в систему и проверяет, существует ли определенная папка или она не работает. (Это в системе 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  
Клинтон Блэкмор
источник

Ответы:

16

Используйте $(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)"
MikeyB
источник
1
+1 решение, специфичное для вашего точного сценария.
Уорнер
2

Это потому, что он собирается заключить ~ 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
сигнализатор
источник
1
+1, я был далеко на этом :-)
Кайл Брандт
set -x - хороший совет. Удаление цитаты из DIR не работает для меня под Bash 3.2.
Клинтон Блэкмор
GNU bash, версия 2.05b.0 (1) -релиз (i486-slackware-linux-gnu) здесь.
Уорнер
1

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так как код выхода там, скорее всего, будет нулевым.

Приостановлено до дальнейшего уведомления.
источник
+1 Это были некоторые из моих трудностей, но с переменными HOME я думаю, что это, возможно, предназначено не для запуска в качестве пользователя USER, поскольку он переопределяет переменную USERNAME с 3-м аргументом сценария (который через меня тоже отключен). Я также склонен думать о целом ||и &&как управление потоком (замена операторов if) в плохом стиле.
Кайл Брандт
Скрипт будет работать через Каспера. Я не уверен, смотрит ли он на коды выхода, но если это так, я хочу любой экземпляр, где скрипт либо удаляет папку кеша, либо не находит тот, который указывает на успешный запуск скрипта, и любой экземпляр, где имя пользователя не было предоставлено (указывая, что скрипт не был вызван при входе в систему или выходе из системы) для отображения в качестве ошибки. Ошибки помечаются красным, когда мы смотрим журналы того, где выполнялся скрипт.
Клинтон Блэкмор
@Clinton: Тогда вы захотите иметь ненулевое число для выходного значения (включая явное exit nв конце скрипта, если провал также является ошибкой. Ноль означает успех, и вы можете использовать разные ненулевые числа для указать различные типы ошибок (это зависит от вас, чтобы определить их значение для ваших собственных целей.) Если вам не нужно отличать один тип ошибки от другого, то exit 1лучше использовать любой во всех случаях для указания общая ошибка.
Приостановлено до дальнейшего уведомления.
Я думаю, что Unix Unix и все то же самое ... кроме случаев, когда это не так! Похоже, что getent не существует на Mac, и обычные пользователи не упоминаются в / etc / passwd. Ответ на dscl предлагает способ получить ту же информацию под OS X. Я ценю ваш ответ и вполне могу изменить скрипт, чтобы не полагаться на ленивую оценку логических операторов.
Клинтон Блэкмор
1

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

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

Пн.
источник
Неплохо подмечено. dscl /Search read /Users/$USERNAME NFSHomeDirectory | cut -f 2 -d " "Похоже, что это правильно для получения информации, которую я получил.
Клинтон Блэкмор