我有以下文件夹/文件的结构,例如
./3DO Company, The - 3DO/goldstar.bin
./3DO Company, The - 3DO/panafz10e-anvil.bin
./Arcade/pgm.zip
./Arcade/skns.zip
./Atari - 400-800/ATARIBAS.ROM
./Atari - 5200/5200.rom
./Atari - 7800/7800 BIOS (E).rom
./Atari - Lynx/lynxboot.img
./Coleco - ColecoVision/coleco.rom
这只是为了按星号排列文件夹/文件,但我需要所有子文件夹上每个文件的符号链接(不是复制,只是我没有足够的可用空间来制作副本)但我需要文件的全名包括特殊字符并且只使用相对路径,而不是绝对路径。
嗯,我知道使用循环,比如“for”是可能的,我有这个想法,但是在 GNU/Linux 中的 bash 下我没有正确的命令来执行此操作。
我搜索的结果是:
./goldstar.bin
./panafz10e-anvil.bin
./pgm.zip
./skns.zip
./ATARIBAS.ROM
./5200.rom
./7800 BIOS (E).rom
./lynxboot.img
./coleco.rom
有人可以帮我吗?
答案1
有了find
,基本解决方案:
# remember to cd to the right directory first
find . -exec ln -s {} ./ \;
注意事项:
基本解决方案将尝试创建指向所有文件,包括目录类型的文件;您可能只想要常规文件的链接,所以这
-type f
是一个好主意。尝试直接链接到位于其下方的文件
.
和其.
自身将会失败(file exists
),因为路径已被占用(边缘情况:如果在find
发现之后但在ln
执行操作之前删除了该文件,则会创建指向其自身的符号链接;比较托克托)。 和GNUfind
-mindepth 2
允许您跳过这些文件;正确答案(可移植地)您可以检查路径中是否至少有两个斜线:-path './*/*'
。
改进后的命令为:
find . -type f -path './*/*' -exec ln -s {} ./ \;
这仍然不是最优的,因为该命令ln
为每个匹配的文件运行单独的命令。如果您的ln
支持-t
,那么-exec ln -s -t ./ {} +
它将起作用。便携改进是:
find . -type f -path './*/*' -exec sh -c 'ln -s "$@" ./' find-sh {} +
重点是-exec … {} +
可以一次处理多个路径名,但{}
必须就在之前+
,所以-exec ln -s {} ./ +
行不通。在 shell 代码中$@
可以位于任何地方。通过使用 shell,我们可以使一个可移植程序ls
处理传递的多个路径名find
。find-sh
解释如下:中的第二个 sh 是什么sh -c 'some shell code' sh
?
如果要创建符号链接并且名称已经在当前工作目录中被使用,那么将不会创建链接并且不会覆盖现有文件。
“已被占用”可能指的是之前创建的符号链接。这意味着,如果有同名的常规文件(显然在不同的目录中),则最多会创建一个同名的符号链接(可能会出现file exists
错误)。符号链接的目标将是该组中的第一个文件。一般来说,你无法轻易预测顺序(我认为任何(?)find
都使用目录顺序,递归地),因此您无法轻易猜出将链接到哪一个同名文件。
要观察正在处理的内容,请-print
在最后添加(ln -v
这是一种替代方法,但不可移植)。最后的可移植命令是:
find . -type f -path './*/*' -exec sh -c 'ln -s "$@" ./' find-sh {} + -print
注意-exec … {} +
总是被评估为真,它所做的不会影响-print
。如果-print
打印某个路径名,这仅表示尝试创建符号链接;这并不意味着尝试成功。如果您只想打印成功链接的路径名,则需要-exec ln -s {} ./ \; -print
。这将起作用,因为-exec … \;
根据内部命令的退出状态 评估 为真或假,因此如果ln
失败则-print
不会打印路径名。