使用 awk 将文件的符号链接列表发送到名称中包含空格的目录?

使用 awk 将文件的符号链接列表发送到名称中包含空格的目录?

我有一个文件名列表,我想为其创建软链接(称之为filenames.txt)。使用时

cat filenames.txt | awk 'system("ln -s "$0" ~/directory\ with\ spaces/subdirectory/")`

当遇到带有空格的任何内容(文件名或目标目录)时,它似乎会卡住。

如何创建这些软链接,而不必先重命名所有内容以确保没有空格?

答案1

注意:此处提供的所有示例都将从文件和文件名列表所在的目录运行。例如,如果它们位于/mnt/Hard\ Drive/some\ files/文件夹中,请确保filenames.txt也存储在那里。从 shell 执行命令时,请先cd /mnt/Hard\ Drive/some\ files/运行示例。

AWK

AWK 的系统调用可用于包含空格的项,但稍微复杂一些。您的原始代码有一些语法错误并且cat在那里毫无用处。不管这个事实如何,正确的方法是首先将命令构建到sprintf()变量中,然后将该变量传递给system().就像这样:

$ ls dir\ with\ spaces/                                                                                                  
$ awk '{cmd=sprintf("fpath=$(realpath -e \"%s\" );%s %s \"$fpath\" \"%s\"",$0,"ln","-s","dir with spaces/");system(cmd) }' filenames.txt                   
$ ls dir\ with\ spaces/                                                                                                  
ascii.txt@  disksinfo.txt@  input.txt@  process info.txt@

BASH(或任何类似 Bourne 的 shell)

阻力较小的路径是通过下面给出的小 shell 脚本:

#!/bin/bash
# uncomment set -x for debugging
#set -x 

# Preferably, the directory should be full path
dir="dir with spaces/"

while IFS= read -r line
do
    if [ "x$line" != "x"  ] && [ -e "$line" ];
    then
        fpath=$( realpath -e "$line" )
        ln -s "$fpath" "$dir"
    fi
done < filenames.txt

正在运行的脚本:

$ ls -l dir\ with\ spaces/                                                                                        
total 0
$ cat filenames.txt                                                                                               
input.txt
ascii.txt
disksinfo.txt
process info.txt
$ ./makelinks.sh                                                                                                  
$ ls -l dir\ with\ spaces/                                                                                        
total 0
lrwxrwxrwx 1 xieerqi xieerqi  9 1月  15 00:47 ascii.txt -> ascii.txt
lrwxrwxrwx 1 xieerqi xieerqi 13 1月  15 00:47 disksinfo.txt -> disksinfo.txt
lrwxrwxrwx 1 xieerqi xieerqi  9 1月  15 00:47 input.txt -> input.txt
lrwxrwxrwx 1 xieerqi xieerqi 16 1月  15 00:47 process info.txt -> process info.txt

它的工作方式很简单:while IFS= read -r line; do . . . done < filenames.txt逐行读取 filenames.txt,每行都进入line变量。我们检查该行是否不为空以及文件是否存在(该脚本将从原始文件和文件列表所在的同一目录执行)。如果这两个条件都成立,我们就会建立链接。

请注意,您应该确保文件以换行符结尾 - 如果最后一行不以换行符结尾(确实发生),则将跳过最后一行,因此不会为该文件创建任何链接。

尽管并不理想且并非没有怪癖,但对于 awksystem()不可用的情况(现在可能很少见),这是一个可行的解决方案。

参数

稍微简单一点的 shell 方法是通过xargsand bash -c '' sh,我们再次使用标志逐行读取 filenames.txt -L1,并将每一行用作 bash 的参数。使用$@我们获取整个命令行参数数组($1、$2、$3...等)并将其转换为字符串变量,然后将其传递给ln.最后sh是设置$0变量。是否有必要这样做还有争议,但这不是本问题的主题,因此让我们跳过它:)

$ xargs -I {} -L1 bash -c 'file="$@";fpath=$(realpath -e "$file"); ln -s "$fpath"  ./dir\ with\ spaces/"$file" ' sh < filenames.txt                      
$ ls dir\ with\ spaces/                                                                                                  
ascii.txt@  disksinfo.txt@  input.txt@  process info.txt@

Python

$ ls dir\ with\ spaces/ 
$ python -c "import os,sys;fl=[f.strip() for f in sys.stdin];map(lambda x: os.symlink(os.path.realpath(x),'./dir with spaces/' + x),fl)" < filenames.txt
$ ls dir\ with\ spaces/                                                                                                  
ascii.txt@  disksinfo.txt@  input.txt@  process info.txt@

其工作方式很简单 - 我们重定向filenames.txt到 python stdin,将每一行读入 list fl,然后运行os.symlink()​​list 中的每个项目。冗长的一句台词,但有效。

珀尔

一个较短的版本是通过 Perl 实现的:

$ perl -lane 'use Cwd "realpath";symlink(realpath($_),"./dir with spaces/" . $_)' < filenames.txt                                                     
$ ls dir\ with\ spaces/                                                                                                  
ascii.txt@  disksinfo.txt@  input.txt@  process info.txt@

答案2

对于此类任务,请勿使用调用外部命令system()或类似函数的脚本语言。它呈现出相同的危险和陷阱如 Bourne Shell 的eval.

xargs让您安全高效地完成此操作:

xargs -d '\n' ln -s -t ~/directory\ with\ spaces/subdirectory/ < filenames.txt

filenames.txt它的作用是通过在您作为参数给出的命令末尾添加 的内容来构建一个命令。然后它执行它。对于上面的示例,它将构建的命令如下所示:

ln -s -t directory file1 file2 ... fileN

有趣的是,您不必引用 filenames.txt 中的任何内容,也不必对目录使用嵌套引用。

答案3

在字符串文字中,使用双反斜杠在传递给 shell 的字符串中获取单个反斜杠,这会导致 shell 按字面解释下一个字符:

awk 'system("… ~/directory\\ with\\ spaces/subdirectory/")'

要引用 shell 代码片段中使用的输入,一种方法是在每个字符之前添加反斜杠。可移植的 awk 并不能让这变得容易(GNU awk 有可以做到的扩展)。或者,在每个元素周围放置单引号,并将元素中的所有单引号替换为'\''。 Awk 使这个解决方案变得简单:只需使用gsub.如果您的 awk 代码片段位于 shell 脚本中,则可以使用反斜杠八进制转义符将单引号放入 awk 字符串文字中。

<filenames.txt awk '{
    gsub(/\047/, /\047\\\047\047/, $0);
    system("ln -s \047" $0 "\047 ~/directory\\ with\\ spaces/subdirectory/");
}'

相关内容