Mac Shell脚本获取用户主目录

9
我需要在我的shell脚本中获取主目录,以便我的同事可以运行它。我的shell非常简单,只是将一些目录复制到另一个目录。我已经尝试使用以下变量:$HOME,$(whoami),甚至是这个:ABSPATH=$(cd "$(dirname "$0")"; pwd)。但是当我像这样使用变量时:
DIR= $ABSPATH/folder/afolder/bfolder/
并运行它,我会收到以下错误信息:
/Users/theUser/Desktop/FusionTest.command: line 14: /Users/theUser/Desktop/folder/afolder/bfolder: 是一个目录 Copying to usage: cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file target_file cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file ... target_directory logout [Process completed]
我正在使用cp -r命令来复制所有文件和目录。我错过了什么?或者我该怎么做?

2
编写Shell脚本时,应始终引用扩展。因此,请使用"$HOME"而不仅仅是$HOME - sorpigal
一个重要的点是,所涉及的用户可能是另一个用户而不是自己,因此$HOME无效...这是服务器端常见的用例。 - Scott Stensland
4个回答

9

正如错误消息所示,-r不是递归复制的正确标志。您需要的标志是-R(标志区分大小写)。至于主目录,$HOME应该总是有效的。


1
GNU版本的cp也允许使用-r参数,但这不是标准写法,在OS X系统中也无法使用,即使很多示例代码都是这样编写的。 - sorpigal
@sorpigal - 对我来说看起来是POSIX标准 - http://pubs.opengroup.org/onlinepubs/009604499/utilities/cp.html - jordanm
2
@jordanm:这定义了“-r”,但并不要求它执行任何有用的操作(“如果指定了-r选项,则行为是实现定义的。”)。我建议避免使用它... - Gordon Davisson
OP的一个重要观点是,所涉及的用户可能是另一个用户而不是自己,因此$HOME无效。 - Scott Stensland

7
谈论以下几点内容:
  1. Mac如何处理HOME目录,以及如果未设置,检索实际的HOME目录的方法。
  2. 如何正确使用命令行参数,因为Linux上的GNU cp命令允许您以错误的方式执行它,这导致了很多问题。
  3. 如何在脚本中使用set -xv进行调试。这将帮助您调试约90%的shell脚本错误。
  4. 我认为您的脚本实际上存在什么问题。不幸的是,由于缺乏信息,这只是一个猜测,但是它是一个相当好的猜测。

Mac如何处理Home目录

Mac OS X是真正的Unix版本。事实上,它比Linux还要更Unix。因此,它应该与几乎所有系统都相当兼容。

Mac home目录存储在/Users目录下。在Mac使用的默认BASH shell中(甚至在旧的Turbo Csh中),您可以使用~作为对$HOME的别名。

 $ echo "$HOME"   # Use quotes around directory and file names to avoid 
 $                # problems with white spaces.
 $ echo ~         # Echos the `$HOME` directory.

如果有人更改了HOME环境变量,你可以使用~userName来获取正确的HOME目录值:

$ echo $HOME
/Users/david
HOME=/tmp
$ echo $HOME
/tmp
$ echo ~
/tmp
$ echo ~david
 /Users/david
$ HOME=~david   #Fixed!

命令行参数

这个错误很可能与你的电脑是否为Mac电脑无关。在几乎任何计算机上,都可能遇到这种标准问题。Mac的cp命令接受-r-R命令。与Linux系统上的GNU cp不同,Mac正确处理命令行参数。

例如,在Linux上,以下命令是可接受的:

$ cp "$dir" "$dir2" -r

在Unix系统上,这可能会失败。您需要在命令后添加-r参数:

$ cp -r "$dir" "$dir2"

如果您像第一个示例中那样执行cp命令,它在Unix上不起作用,在Mac上也不起作用。

调试Shell脚本

现在,我们已经解决了一些基础问题,让我们看看如何调试您的问题...
在第14行之前(也许在前面几行),加入set -xv。这将打开脚本调试。参数-x将在执行该行之前回显带有插值的命令行。这样,您就可以看到实际执行的命令是什么。
参数-v将回显未插值的命令行。这样,您就可以看到命令的样子,可能会检测到环境变量设置中的错误。
下面是一个简单的脚本:
 name="David"
 echo "Hello $name!"

执行该脚本,我们得到:
 $ test.sh
 Hello David!
 $

现在,我将执行以下操作:
 set -xv
 name="David"
 echo "Hello $name!"
 set +xv     #Turns off the -xv flags

现在,我会得到这个:

 $ ./test.sh
 name="David"
 + name=David
 echo "Hello $name!"
 + echo 'Hello David!'
 Hello David!

另一个巧妙的技巧是将PS4设置为“${LINENO}:”。这将在每行前加上行号,而不仅仅是像上面那样加上+

可能的解决方案

我感觉问题出在目录名中有空格。请用引号引用目录和文件名,以防止这些空格给您带来麻烦:

my_pictures=$HOME/"My Pictures"
cp -R $my_pictures $target     #Won't work due to the space between "My" & "Pictures"
cp -R "$my_pictures" "$target" #The quotes solve the problem.

结论

不要担心你的 Mac 是“不同”的。它只是另一个 Unix 系统。确保正确使用命令行参数 cp -R $dir1 $dir2,而不是 cp $dir $dir2 -R。使用 set -xv 帮助你定位错误,并养成引用环境变量的习惯,因为环境变量的值中的空格或制表符可能会导致程序失败。


关于 Mac OS X - Lion 的说明

根据 man 页,-r 不再作为选项进行文档记录。但它仍然有效。根据 man 页:

cp 实用程序的历史版本具有 -r 选项。此实现支持该选项;但是,强烈不建议使用它,因为它无法正确复制特殊文件、符号链接或管道。

然而,cp -R 应该在 Linux 上都能正常工作,并在 Mac OS X 中作为递归复制标志进行了记录。


1
OSX 10.7.4及以上版本的cp命令不支持-r选项。我记得早些时候也是这样。 - You
我刚刚检查了一下,它使用的是cp -R而不是cp -r,但在底部说:“_cp实用程序的历史版本有一个-r选项。该实现支持该选项;然而,强烈不建议使用它,因为它不能正确地复制特殊文件、符号链接或管道。_”基本上,使用cp -r确实可以工作,但并不被鼓励。GNU在Linux上同时支持-r-R。那就使用-R吧。 - David W.
@DavidW。在 Automator 的“运行 Shell 脚本”操作中, $HOME 没有被设置。你如何在不硬编码用户名的情况下获取它? - mb21
@DavidW。你说得对,现在它也对我有效!奇怪的是,一开始我只能在单击“运行”按钮时使用 $HOME,而不能在通过“服务”菜单调用时使用,但现在它也在那里发挥作用 :) - mb21
强烈不建议使用此选项,因为其行为取决于实现方式。在FreeBSD中,-r是-RL的同义词,并且除非被其他标志修改,否则功能相同。历史上的-r实现不同,因为它们将特殊文件复制为普通文件,同时重新创建层次结构。https://www.freebsd.org/cgi/man.cgi?query=cp&sektion=1#end - Nemo
显示剩余3条评论

2

我不喜欢在脚本中使用 ~ 和 $HOME,在某些情况下,这可能取决于您使用的 shell - 也许在 OSX 上没有问题,但我不在乎。

系统在某个地方记录了用户主目录的位置,并从中设置 $HOME。

具体来说,我通常试图找到另一个用户的主目录(再次强调不喜欢在脚本中使用 ~ 缩写 - 这是个人偏好)对于 OSX,我使用以下命令 -

 finger $username | awk '/^Directory/ {print $2}'

在其他地方,我通常会使用grep /etc/passwd命令,但是仔细考虑后,finger命令可能更加通用。

这是我的快速而简单的答案。如果你更了解OSX内部,就会有更好的解决方案,但是关心程度呢?这对我有效。


如果用户目录中有空格,有没有办法让它工作? - coderforlife

1

$HOME 应该被设置,但不能保证。用户有可能覆盖此变量。另一个选项是使用 getent

IFS=: read -r _ _ _ _ _ homedir _ < <(getent passwd myuser)
echo "$homedir"

3
使用以下命令查找用户的主文件夹路径:dscl . -read /users/$user | grep NFSHomeDirectory - David W.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接