复制文件列表


35

我有一个文件列表,用文件中的空格分隔list.txt。我想将它们复制到新文件夹中。

我试着做:

cp `cat list.txt` new_folder

但它没有用。

你会怎么做?

更新:

感谢您提出的所有非常有趣的答案。我没有要求那么多:)

在第二次尝试解决方案之后,似乎已cp打印使用帮助,因为该文件夹new_folder尚不存在!抱歉,这似乎是一个愚蠢的错误...当我之前创建文件夹时,它可以工作。

但是,我从您的所有答案和解决方案中学到了很多东西,谢谢您花时间编写它们。


让我澄清一件事:列表将所有文件名放在一行中,仅用空格分隔?还是每个名称都在单独的行中?
Telemachus

全部合二为一,用空格隔开。道格的解决方案有效:)谢谢您的回答。
克劳斯

真有趣……我在一行中测试了我的解决方案,其中每个名称都在单独的行中。
道格·哈里斯

“没有用”不是很有意义。究竟是怎么回事?这对我来说可以。
暂停,直到另行通知。

抱歉:它显示了cp使用帮助。请参阅上面的更新。
克劳斯

Answers:


34

尝试使用xargs:

cat list.txt | xargs -J % cp % new_folder

更新:

我是在OS X上执行此操作的,该版本与GNU / Linux版本不同。xargs来自GNU findutils的那个没有,-J但是它有-I一个类似的东西(正如Dennis Williamson在评论中指出的那样)。xargs的的OS X版本的既有-I-J具有略微不同的行为-无论是将这种原来的问题的工作。

$ cat list.txt
one
two.txt
three.rtf

$ cat list.txt | xargs -J % echo cp % new_folder
cp one two.txt three.rtf new_folder

$ cat list.txt | xargs -I % echo cp % new_folder
cp one new_folder
cp two.txt new_folder
cp three.rtf new_folder

3
xargs没有-J-I,这似乎做同样的事情。xargs您有什么版本的操作系统和发行版?
暂停,直到另行通知。

我在Mac OS X 10.6(又名雪豹)上进行了测试,因此它是操作系统随附的任何xargs版本。运行xargs --version返回使用错误消息。可能是xargs的BSD版本。使用的-I-J有微妙的不同。我将使用格式正确的示例更新我的答案。
道格·哈里斯

是的,我正在使用Mac OSX 10.6。感谢您关于-J和的精确度-I:)
克劳斯

这是在运行命令之前使用echo检查命令的一个很好的技巧。
利亚姆

1
仅使用上面的命令进行必要的更改,如何复制文件以及创建文件所在的目录和子目录(以斜杠分隔)?
Vicky Dev

54

完全不需要cat

xargs -a list.txt cp -t new_folder

或使用长选项:

xargs --arg-file=list.txt cp --target-directory=new_folder

这是一些shell版本。请注意,某些变量未加引号或for专门使用了循环,因为OP指定文件名用空格分隔。其中某些技术不适用于文件名包含空格的每行文件名输入。

重击:

for file in $(<list.txt); do cp "$file" new_folder; done

要么

cp $(<list.txt) new_folder

sh(或Bash):

# still no cat!
while read -r line; do for file in $line; do cp "$file" new_folder; done; done < list.txt

要么

# none here either
while read -r line; do cp $line new_folder; done < list.txt

要么

# meow
for file in $(cat list.txt); do cp "$file" new_folder; done

要么

# meow
cp $(cat list.txt) new_folder

新手提问:为什么-t需要xargs -a list.txt cp -t new_folder
杨艺博

1
@YiboYang:-t是的选项cp。它将所有源参数复制到指定的目标目录中。在此,它允许在xargs命令添加的源之前指定目标。
暂停,直到另行通知。

这很有用,但是如果list.txt包含混合的文件和文件夹路径怎么办?在这种情况下,该命令将无法正常工作。它报告“省略目录...”,如果在cp命令中指定了-R,则在遇到列表中的第一个文件后立即报告“不是目录”错误。
Bemipefe

7

我之前在这里有一个令人尴尬的回旋答案,但是Denis的答案提醒我,我错过了最基本的东西。因此,我删除了原始答案。但由于没有人说过这个非常基本的内容,因此我认为值得将其放在这里。

最初的问题是“我有一个文本文件,其中包含用空格分隔的文件名列表。如何将它们复制到一个目标目录中。” 起初,这似乎很棘手或复杂,因为您认为必须以某种特定方式从文件中提取项目。但是,当shell处理命令行时,它要做的第一件事是将参数列表分隔为令牌,并且(这里没有人直接说过这一点)用空格分隔令牌。(换行符也分隔标记,这就是为什么道格·哈里斯(Doug Harris)使用换行符分隔列表进行测试的结果相同的原因。)也就是说,shell期望并且已经可以处理以空格分隔的列表。

因此,您要做的就是将以空格分隔的列表(您已经拥有的列表)放在命令中的正确位置。您的命令对此有所不同:

cp file1 file2 file3...file# target

唯一的麻烦是您希望从文本文件中获取文件列表1到#。

正如Dennis在其评论中指出的那样,您的原始尝试(cpcat list.txt new_folder)应该已经起作用。为什么?因为内部命令cat list.txt首先由外壳程序处理,然后扩展为file1 file2 file3...file#,这恰恰是外壳程序在该命令部分期望和想要的。如果它不起作用,那么(1)您有错字或(2)您的文件名有些奇怪(它们有空格或其他不寻常的字符)。

所有Dennis答案都能工作的原因仅仅是因为它们提供了要处理的必要文件列表cp,并将该列表放在整个命令中的位置。同样,命令本身的结构如下:

cp list-of-files target_directory

很容易看出这一切在此版本中如何结合在一起:

cp $(<list.txt) new_folder

$()使外壳程序在括号内运行命令,然后在较大行中的该点替换其输出。然后,外壳将整个线贯穿起来。顺便说一句,$()是您对反引号(`)所做的工作的更新版本。下一步:<是文件重定向运算符。它告诉外壳将内容转储list.txt到标准输入中。由于$()首先处理位,因此分阶段进行以下操作:

  1. cp $(<list.txt) new_folder # split line into three tokens: cp, $(<list.txt), new_folder
  2. cp file1 file2 file3...file# new_folder # substitute result of $(<list.txt) into the larger command

显然,步骤2只是cp您想要的普通命令。

我意识到我经常殴打这匹(也许已经死了)的马,但是我认为这是值得做的。了解究竟壳如何处理命令可以帮助您更好写,并简化了很多。它还将向您显示问题可能隐藏的位置。例如,在这种情况下,我要问的第一个问题应该是关于有趣的文件名或可能的错字。无需杂技。


@Klaus非常欢迎。坦率地说,在阅读了丹尼斯的答案并意识到我完全错过了问题的关键部分之后,我对自己大为恼火。您喜欢的解决方案也来自那里。
Telemachus

这是一个超旧的答案,但是如果您需要重新创建文件夹结构,则需要--parents标记。您可以使其冗长,-v以记录复制的内容。如果要保留权限,则需要-aor -p标志。即,cp --parents -av $(<list.txt) new_folder。所有这些都假设您的文件列表实际上是所有文件,否则您可能还需要-r标记来递归。
dhaupin's

@dhaupin实际上,OP指出他正在使用macOS。因此,就其价值而言,该--parents标志在那里不存在。他们的(BSD派生的)版本cp仅提供短选项,而不提供GNU风格的长选项。对于BSD样式cp,递归复制的标志是大写的-R,而不是小写的。
Telemachus
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.