多线方式 cp

多线方式 cp

在带有 Bash 3.2.52(2) 的 CentOS 中,我必须将许多文件(不是全部)从一个目录复制到另一个目录。

我可以创建一个很长的一行,例如,cp /"$HOME"/dir1/{file1,fil2} ... /"$HOME"/dir2但因为 dir1 中有很多文件,所以我更喜欢以多行方式复制文件。
编辑:我手动创建列表。

如何才能做到这一点?
我更喜欢无反斜杠的解决方案 |我没有找到任何线索man cp|也许只有这里文档?

答案1

files=(
    file1 file2 "the goose incident.png"
    "another file"
    file3 file-4 "fifth file" file6
    "this is file7 with a
newline in the middle of the name" )

cd ~/dir1 &&
cp "${files[@]}" ~/dir2

files这会将列表中提到的名称复制~/dir1~/dir2

列表中元素之间的换行符files并不重要,除了最后一个元素中的换行符,它是嵌入了换行符的文件名(只是为了表明您也可以拥有这些,而不会出现问题)。

该列表也可以写成

files=(
    file1
    file2
    "the goose incident.png"
    "another file"
    file3
    file-4
    "fifth file"
    file6
    "this is file7 with a
newline in the middle of the name"
)

或作为

files=( file1 file2 "the goose incident.png" "another file" file3 file-4
        "fifth file" file6 "this is file7 with a
newline in the middle of the name" )

答案2

这个怎么样:

#!/usr/bin/env bash

files="1
2
3
4
4
5
6
7
8
9 10 11"

IFS=$'\n'
for file in $files
do
    touch "$file"
done

只需更换touch "$file"您需要的即可。此解决方案的缺点是,如您所见,它将为每个文件创建一个新进程strace,因此对于大量文件来说会很慢:

$ strace -f ./cp-here-doc.sh  |& grep 'execve("/usr/bin/touch"'
[pid 17917] execve("/usr/bin/touch", ["touch", "1"], [/* 63 vars */]) = 0
[pid 17918] execve("/usr/bin/touch", ["touch", "2"], [/* 63 vars */]) = 0
[pid 17919] execve("/usr/bin/touch", ["touch", "3"], [/* 63 vars */]) = 0
[pid 17920] execve("/usr/bin/touch", ["touch", "4"], [/* 63 vars */]) = 0
[pid 17921] execve("/usr/bin/touch", ["touch", "4"], [/* 63 vars */]) = 0
[pid 17922] execve("/usr/bin/touch", ["touch", "5"], [/* 63 vars */]) = 0
[pid 17923] execve("/usr/bin/touch", ["touch", "6"], [/* 63 vars */]) = 0
[pid 17924] execve("/usr/bin/touch", ["touch", "7"], [/* 63 vars */]) = 0
[pid 17925] execve("/usr/bin/touch", ["touch", "8"], [/* 63 vars */]) = 0
[pid 17926] execve("/usr/bin/touch", ["touch", "9 10 11"], [/* 63 vars */]) = 0

您可以在最终脚本中仅使用一次xargsruntouch或,以使脚本运行得更快:cp

#!/usr/bin/env bash

files="1
2
3
4
4
5
6
7
8
9 10 11"

echo "$files" | tr '\n' '\0'  | xargs -0 touch

结果:

$ strace -f ./cp-here-doc.sh  |& grep 'execve("/usr/bin/touch"'
[pid 18290] execve("/usr/bin/touch", ["touch", "1", "2", "3", "4", "4", "5", "6", "7", "8", "9 10 11"], [/* 63 vars */]) = 0

另请注意,在 Linux 上至少文件名可以包含换行符,因此如果至少一个文件名包含换行符,则必须选择另一个分隔符。

OP问:

IFS=$'\n' 是什么意思

这意味着一个字面上的新行。您可以阅读以下内容man bash

   Words of the form $'string' are treated specially.  The word
   expands to string, with backslash-escaped char- acters replaced
   as specified by the ANSI C standard.  Backslash escape
   sequences, if present, are decoded as follows:
          \a     alert (bell)
          \b     backspace
          \e
          \E     an escape character
          \f     form feed
          \n     new line

您的最终脚本可能如下所示:

#!/usr/bin/env bash

files="1
2
3
4
5
6
7
8
9 10 11"

echo "$files" | tr '\n' '\0'  | xargs -0 cp -t {} dir

我真的很愿意去xargs这样的选择很多更快更安全 - 请参阅下面 Kusalananda 的评论。我没有使用cp命令运行测试,但当我测试touch在列表中创建文件时$(seq 1 1000000),只花了 16 秒xargs,但 for 循环版本却花了 42 分钟。另外,令我惊讶的是,xargs能够将参数列表拆分为多个命令,这样Argument list too long就不会成为问题。

相关内容