我是 bash 脚本编写的新手,想要制作一个脚本来获取用户输入并将目录中的所有文件硬链接到另一个目录。到目前为止我有这个:
read -p "What directory do you want to link? " dir
items=$(ls -m $dir -Q | tr -d '\n'' ')
read -p "Where do you want to link the files? " outdir
echo "Linking files: $items to: $outdir"
read -p "Continue? (y/N) " confirmation
if [ $confirmation == "y" ] || [ $confirmation == "Y" ]; then
ln $dir/{$items} $outdir
else
kill -SIGTERM $$
fi
当我输入/home/ethan/test
并/home/ethan/test2
得到ln: failed to access '/home/ethan/test/{"test1","test2"}': No such file or directory
另外,它的内容/home/ethan/test
是两个文件,test1
我test2
在 Arch 上。
谁能帮我弄清楚需要改变什么才能使其正常工作?谢谢。
答案1
对于像这样的简短实用程序脚本,我建议不是使其具有互动性。用户可能已经确切地知道他们想要以您所描述的方式复制哪个目录,并且允许用户在命令行上使用制表符完成和/或文件名通配使得他们更有可能获得与必须从记忆中输入参数相比,参数是正确的。
另外,我会避免用于ls
除查看文件列表。不要ls
在脚本中使用来读取文件名。最好只使用文件名通配模式。在其他问答中对此进行了详细讨论:为什么*不*解析`ls`(以及该怎么做)?
要退出脚本,可以让执行运行到最后,或者使用exit
.不要使用kill
.另外,一般来说,双引号所有变量扩展以避免分词和不需要的文件名通配(请参阅什么时候需要双引号?)。
您的具体错误是由于尝试在大括号扩展中使用 shell 变量造成的。这是不可能的bash
(参见例如将大括号和变量扩展合并在一行中)。
如果用户想要使用名称以破折号开头的目录,您还会遇到更多问题。
我不会使用ls
它的输出并将其组合到某种在大括号扩展中使用的列表(这会取消包含逗号、换行符和可能其他一些字符的文件名),而是使用rsync
它的--link-dest
选项,将其包装到 shell 中为了方便起见,函数或 shell 脚本:
#!/bin/sh -
# The two expected arguments are
# 1. an existing source directory, and
# 2. a possibly existing destination directory
if [ ! -d "$1" ]; then
printf 'Not a directory: %s\n' "$1" >&2
exit 1
fi
if [ -e "$2" ] && [ ! -d "$2" ]; then
printf 'Not a directory: %s\n' "$2" >&2
exit 1
fi
rsync -a --link-dest="$(readlink -f -- "$1")" -- "$1/" "$2"
我们首先要确保用户提供了有效的参数。对于此脚本,这意味着第一个参数(源)必须是一个现有目录,而第二个参数(目标)必须不存在或也是一个目录。
我们使用来确保源目录的绝对路径与选项readlink -f
一起使用,否则它将解释相对于目标目录路径的路径。该实用程序不是标准的,但很常见。--link-dest
rsync
readlink
以这种方式使用rsync
可确保在目标目录路径处递归地重新创建源目录,并且(如果源和目标位于同一文件系统上)非目录文件是硬链接的。如果源路径和目标路径位于不同的文件系统上,则源目录路径是递归的复制的相反,不创建任何硬链接。
请注意,我们在调用中的源参数末尾添加了一个斜杠rsync
。我们这样做是为了复制源目录的内容,而不是目录本身。
我将脚本作为/bin/sh
脚本编写,而不是作为bash
脚本编写,因为它没有使用任何特殊bash
功能。
测试:
$ tree src
src
|-- data.dat
|-- subdir1
| `-- myfile.txt
`-- subdir2
`-- myfile.txt
2 directories, 3 files
$ linkcp src dst1
请注意每个文件的链接计数是 2:
$ ls -l dst1
total 8
-rw-r--r-- 2 myself wheel 0 Apr 6 07:47 data.dat
drwxr-xr-x 2 myself wheel 512 Apr 6 07:47 subdir1
drwxr-xr-x 2 myself wheel 512 Apr 6 07:47 subdir2
$ ls -l dst1/subdir*
dst1/subdir1:
total 0
-rw-r--r-- 2 myself wheel 0 Apr 6 07:47 myfile.txt
dst1/subdir2:
total 0
-rw-r--r-- 2 myself wheel 0 Apr 6 07:47 myfile.txt
$ linkcp src dst2
src
当我们制作该结构的另一个副本时,链接计数现在增加到 3:
$ ls -l dst2
total 8
-rw-r--r-- 3 myself wheel 0 Apr 6 07:47 data.dat
drwxr-xr-x 2 myself wheel 512 Apr 6 07:47 subdir1
drwxr-xr-x 2 myself wheel 512 Apr 6 07:47 subdir2
$ ls -l dst2/subdir*
dst2/subdir1:
total 0
-rw-r--r-- 3 myself wheel 0 Apr 6 07:47 myfile.txt
dst2/subdir2:
total 0
-rw-r--r-- 3 myself wheel 0 Apr 6 07:47 myfile.txt