【问题标题】:Mac Shell script get home directoryMac Shell 脚本获取主目录
【发布时间】:2012-06-25 01:59:12
【问题描述】:

我需要在我的 shell 脚本中获取主目录,以便我的同事可以运行它。我的 shell 所做的非常简单,它只是将一些目录复制到另一个目录。我用过:

$HOME,
$(whoami)
即使这样:
ABSPATH=$(cd "$(dirname "$0")"; pwd),

但是当我像这样使用变量时:

DIR= $ABSPATH/folder/afolder/bfolder/

运行它我会收到这个:

/Users/theUser/Desktop/FusionTest.command:第14行:/Users/theUser/Desktop/folder/afolder/bfolder:是一个目录
复制到 用法:cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] 源文件目标文件 cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] 源文件 ... 目标目录 登出
[处理完成]

我正在使用命令 cp -r 复制所有文件和目录。

我怎么不见了?或者我该怎么做?

【问题讨论】:

  • 在编写 shell 脚本时应该始终引用扩展名。所以,请使用"$HOME" 而不仅仅是$HOME
  • 一个重要的点是有问题的用户可能是另一个用户而不是自己所以 $HOME 无效......服务器端的一个常见用例

标签: macos bash cp home-directory


【解决方案1】:

我不喜欢在脚本中使用 ~ 并且 $HOME 有时会失败,具体取决于您使用的外壳 - 也许不是在 OSX 上,但我不在乎..

系统以某种方式记录了用户的主目录的位置,并从中设置了 $HOME。

具体来说,我通常试图找到另一个用户的主目录(同样不喜欢脚本中的〜缩写 - 称之为偏好) 对于 OSX,我使用这个 --

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

在其他任何地方,我通常 grep /etc/passwd,但仔细想想,finger 可能会更通用。

这是我快速而肮脏的答案。如果您更好地了解 OSX 内部,将会有更好的解决方案 - 但关心因素?这对我有用

【讨论】:

  • 如果用户的目录中有空格,有没有办法让它工作?
【解决方案2】:

在这里谈几件事:

  1. Mac 如何处理HOME 目录以及在未设置实际HOME 目录的情况下检索它的方法。
  2. 如何正确使用命令行参数,因为 Linux 上的 GNU cp 命令允许你以错误的方式进行操作,这导致了很多问题。
  3. 如何在脚本中使用set -xv 进行调试。这将帮助您调试您遇到的大约 90% 的 shell 脚本错误。
  4. 我认为您的脚本实际上是错误的。不幸的是,由于缺乏信息,这是一个疯狂的猜测,但这是一个很好的猜测。

Mac 如何处理主目录

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

Mac 主目录存储在/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!

命令行参数

该错误可能与您计算机的Macness 无关。这可能是您在几乎任何计算机上都能找到的标准问题。 Mac 的cp 命令采用-r-R 命令。与 Linux 系统上的 GNU 的 cp 不同,Mac 处理命令行参数正确

例如,在 Linux 上,这是可以接受的:

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

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

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

如果您像第一个示例一样执行cp 命令,它将无法在 Unix 上运行,也无法在 Mac 上运行。

调试 Shell 脚本

现在,我们已经完成了一些初步准备,让我们看看如何调试您的问题...

set -xv 放在第 14 行之前(可能在前面几行以确定)。这将打开脚本调试。 -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 的注意事项

根据手册页,-r 不再记录为选项。但是它确实有效。根据手册页:

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

但是,cp -R 应该可以在 Linux 上运行,并且在 Mac OS X 中作为递归复制标志记录。

【讨论】:

  • OSX cp 至少在 10.7.4 上无法识别 -r 标志。我记得之前也是这样。
  • 我刚刚检查过了。它使用cp -R 而不是cp -r,但在底部说“cp 实用程序的历史版本有一个 -r 选项。此实现支持该选项;但是,强烈建议不要使用它,因为它不正确复制特殊文件、符号链接或文件。"。基本上,使用cp -r 确实有效,但不鼓励使用。 Linux 上的 GNU 支持-r-R。然后使用-R
  • @You BTW,感谢您指出 cp -r 在 Mac OS X 上显然不再受到鼓励。自从 cp 大约 15 年前开始使用它以来,我一直在使用它(在此之前,您使用cpio 复制目录)。我看到 cp -r 在我的系统上运行,我有 10.7.4,但我也是从 Snow Leopard 升级的,所以这可能是结果。您可以改用cp -R,这也适用于Linux。我更改了答案以反映这一点。
  • @DavidW。在 Automator 中“运行 Shell 脚本”动作 $HOME 未设置。如果不对用户名进行硬编码,您将如何获得它?
  • @mb21 我不使用 Automator,所以我不确定 Automator 在运行时会做什么。 lognamewhoami 是否返回任何内容?您可以使用它来获取用户名,然后使用 finger 获取 $HOME 目录。
【解决方案3】:

$HOME 应该设置,但不能保证。用户可以覆盖此变量。另一种选择是使用getent

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

【讨论】:

  • 用这个找到用户的主目录dscl . -read /users/$user | grep NFSHomeDirectory
【解决方案4】:

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

【讨论】:

  • cp 的 GNU 版本也允许 -r,但这不是标准的,并且不会在 OS X 上找到,即使可能会以这种方式编写许多示例代码。
  • @sorpigal - 在我看来是 POSIX - pubs.opengroup.org/onlinepubs/009604499/utilities/cp.html
  • @jordanm:它定义了-r,但不需要它做任何有用的事情(“如果指定了 -r 选项,则行为是实现定义的。”)。我会避免它...
  • OP 的一个重要点是有问题的用户可能是另一个用户而不是自己,所以 $HOME 无效