将用户名传递给脚本后,找到用户的主目录


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。如果在shell上键入:

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  

Answers:


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)"

1
+1具体针对您的确切脚本的解决方案。
华纳2010年

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是一个不错的提示。在bash 3.2下,从DIR删除引用对我不起作用。
克林顿·布莱克莫尔

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的身份运行,因为他使用脚本的第3个参数覆盖了USERNAME变量(也通过我关闭了)。我也倾向于认为整体||&&流程控制(替换if语句)是不好的风格。
凯尔·布​​兰特

该脚本将通过Casper运行。我不确定是否要查看退出代码,但是如果要查看退出代码,则我希望该脚本删除缓存文件夹或找不到一个实例来指示脚本成功运行的任何实例,以及该实例的任何实例。未提供用户名(表示未在登录或注销时调用脚本)以显示为错误。当我们查看脚本运行的日志时,错误标记为红色。
克林顿·布莱克莫尔

@Clinton:然后,您将希望有一个非零的退出值(exit n如果失败也包含一个错误,请在脚本末尾包含一个显式的值。零表示成功,您可以使用其他非零数来表示)指出不同类型的错误(取决于您自己的目的,取决于您的意思)如果您不需要将一种错误类型与另一种类型的错误区分开,那么exit 1在任何情况下都可以使用它来表示错误一般错误
暂停,直到另行通知。2010年

我猜unix是unix,并且都是一样的……除非不是这样!看来Mac上不存在getent,并且/ 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 " "似乎是正确的获取我所追求的信息。
克林顿·布莱克莫尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.