符号链接 (ln) 文件夹无法按预期工作

Symlink (ln) folders doesn't work as expected

我正在创建一个脚本,它基本上查找所有以 .symlink 结尾的内容,并尝试创建指向 $HOME 目录的符号链接,删除 symlink 部分并添加 dot 在名字前面。

下面是显示如何创建目标的行。

dst="$HOME/.$(basename "${src%.*}")"

为此创建了两个函数

create_symlink () {
  local src= dst=

  # Create the symlink
  ln -s -i "$src" "$dst"
  message "Linked  -------> " # This just use echo
}

run_symlinks() {
  message "Creating Symlinks"

  local root=

  # Find the files/folder and try to do the symlink
  for src in $(find -H $root -name '*.symlink')
  do
    dst="$HOME/.$(basename "${src%.*}")"

    create_symlink "$src" "$dst"
  done
}

问题

我有一个名为 atom.symlink 文件夹,该文件夹基本上为我的环境提供了一些配置,其中包含 Atom 文本编辑器的文件。当我 运行 run_symlinks 功能文件夹 正在同步但在错误的位置。

message "Linked -------> "的输出是:

Linked $HOME/.dotfiles/src/symlinks/atom.symlink -------> $HOME/.atom

但是当我查看文件夹时,符号链接实际上是 $HOME/.atom/atom.symlink 而不是 .atom 文件夹。

注意: $HOME/.atom 文件夹不为空,我需要弄清楚如何制作此脚本而不用担心文件夹为空。

我试图在 Google 上找到答案,但我什至不知道如何提出这个具体问题。

来自man ln

   ln [OPTION]... [-T] TARGET LINK_NAME   (1st form)
   ln [OPTION]... TARGET                  (2nd form)
   ln [OPTION]... TARGET... DIRECTORY     (3rd form)
   ln [OPTION]... -t DIRECTORY TARGET...  (4th form)

   In  the 1st form, create a link to TARGET with the name LINK_NAME.  In the
   2nd form, create a link to TARGET in the current directory.   In  the  3rd
   and  4th  forms,  create  links  to each TARGET in DIRECTORY.  Create hard
   links by default, symbolic links with --symbolic.  By default, each destination
   (name  of  new link) should not already exist.  When creating hard
   links, each TARGET must exist.  Symbolic links can hold arbitrary text; if
   later  resolved,  a relative link is interpreted in relation to its parent
   directory.

在你的脚本第一次迭代后,你有

$HOME/.atom/ -> $HOME/.dotfiles/src/symlinks/atom.symlink # This is the first form in above man page snippet.

在第二次迭代中,您属于第三种形式,因为目标已经存在并且在符号链接取消引用之后,它是一个目录。

所以,命令 运行 是相同的:

  ln -s -i $HOME/.dotfiles/src/symlinks/atom.symlink $HOME/atom

唯一不同的是,在第二次迭代中,目标是现有目录(取消引用后)。

因此您应该先删除目标 (rm -f "$dst"),然后创建一个符号链接。还好ln自己能搞定:

将您的代码更改为:

ln -sfn "$src" "$dst" # Note that with -f & -n in place, usage of -i is moot.

补充,很好地解释了你的方法的问题:

希望在 $HOME 中保留可能预先存在的(非 symlink)目标文件夹并添加 到它们的内容 需要一种 根本不同的方法:

解决这个问题的唯一方法是避免 symlinking *.symlink 目录本身;相反,这些目录中的 文件 必须 单独 symlinked 到目标文件夹,该文件夹可以是一个预先存在的文件夹或一个必须按需创建为常规文件夹。
这是保证目标文件夹的现有内容不会(总是)丢失的唯一方法:

while IFS= read -r f; do
  # Strip $root from the file path, then remove suffix '.symlink' and add prefix '.'
  # to the path commponent(s) to get the link path.
  link="$HOME/$(sed 's#\([^/]\{1,\}\)\.symlink\(/\{0,1\}\)#.#g' <<<"${f#$root/}")"
  # Make sure that the parent path exists, creating it on demand (as *regular* folder(s)).
  mkdir -p -- "$(dirname -- "$link")"
  # Now we can create the symlink.
  echo "symlinking [$link] <- [$f]"
  # Note the need to redirect from `</dev/tty` so as not
  # to suppress the interactive prompt (`-i`).
  ln -s -i "$f" "$link" </dev/tty
done < <(find -H "$root" -type f \( -path '*.symlink' -or -path '*.symlink/*' \)) 

方法如下:

  • find 命令仅查找 文件,即那些 自身 名为 *.symlink 的文件,以及 目录 中名为 *.symlink 的文件(无论文件本身具有什么后缀)。

  • 对于每个文件,目标symlink路径是通过删除$root路径前缀,然后删除后缀.symlink并添加前缀[=来确定的18=] 匹配路径组件。

  • 每个目标 symlink 路径的父路径的存在通过 mkdir -p 确保:任何现有的路径组件都按原样保留,任何不存在的路径组件都保留创建为常规文件夹。

  • 一旦确保/建立了 symlink 的目标文件夹,就可以调用 ln 命令。

    • 请注意 -i - 在 link 的路径已经存在的情况下呈现一个交互式提示要求替换 - 要求标准输入是 终端 为了踢进来;因此,鉴于 stdin 被重定向到提供 find 输出的进程替换,需要 </dev/tty 来显示提示。