Arrrow Asked: 2020-01-28 07:38:04 +0800 CST2020-01-28 07:38:04 +0800 CST 2020-01-28 07:38:04 +0800 CST sh 替代 bash cp {file,file} 772 在脚本中,如果我放在#!/bin/bash顶部,这将正常工作: cp directory/{foo,bar} destination 但是,使用 sh,这将给出错误“没有这样的文件或目录”。在 sh 中,是否有另一种方法可以用一个命令完成相同的最终目标? bash cp sh 2 个回答 Voted Best Answer Eliah Kagan 2020-01-28T07:53:22+08:002020-01-28T07:53:22+08:00 正如您所观察到的,并非所有 shell 都支持大括号扩展。 方式一:两条独立的路径(即两次写前缀) 如果您的目标真的只是在一个命令中完成,那么一种简单的方法是: cp directory/file1 directory/file2 destination 该cp命令支持多个源参数,后跟一个目标参数。事实上,这就是在 Bash 中为您工作的命令所发生的事情。Bash 执行大括号扩展,它将您一直使用的命令转换为上面显示的命令。也就是说,当您cp directory/{file1,file2} destination在 Bash 中运行时,该cp命令永远不会看到任何花括号。 这样的命令简单易懂。我建议考虑一下。 方式2:前缀的变量 但是,如果您的目标是避免重复自己,那么您可能不想那样做,尤其是如果directory实际上是一条很长的路。毕竟,如果您必须稍后在脚本中编辑路径,则必须对这两个事件进行更改。 您可以将directory(或任何可能更长的路径)存储在shell 变量中: dir='directory' cp "$dir/file1" "$dir/file2" destination $dir是参数扩展。shell 将其替换为变量中保存的值dir。使用参数扩展时,您不应省略双引号——除非在极少数情况下您希望执行字段拆分和通配。(这也适用于 Bash。) 这是两个命令,但如果您的实际目标是避免重复自己,那可能就是您想要的。你可以写 dir='directory'; cp "$dir/file1" "$dir/file2" destination 使其成为“一个命令”,但这并不短,而且可读性也差一些。 (请注意,您不能简单地删除;那里。这样做的结果将是一个格式正确的命令,但它不会执行您想要的操作。它会运行cp命令,将一个使用该值调用的环境变量传递到命令的环境中. 由于扩展的是shell而不是命令,因此这是行不通的。)dirdirectorycpcpdir 方式3:cdig到前缀 如果您的脚本在执行此操作后没有做任何其他事情,或者当前目录对于后续命令无关紧要,那么如果destination是绝对路径,您可以使用: cd directory cp file1 file2 destination 但我不建议将其用于脚本,除非您的脚本执行从该目录运行受益的后续操作,或者当它们从该目录运行时可以更简洁或简单地编写。除非运行脚本的目录对脚本的用途有意义,否则依赖它会使脚本更难推理;此外,如果cd命令成功但cp命令失败,则生成的错误消息可能无法完全说明。相反,我提到了cding 到源目录的技术,因为它是交互式shell 使用的有用模式。 (正如Hannu 指出的那样,您可以将任意数量的命令放在一起以在( )subshell中运行它们,并且对 shell 环境的更改,包括 的效果cd,在命令运行后不会持续存在。这是一种广泛有用的技术,我没有提到它是一个重大的遗漏。但是请注意,这并不能减轻与为 . 的相对路径相关的任何问题。destination) 请注意,如果cd命令失败,其他命令通常仍会运行。这可能会复制您不想复制的不同文件(如果在运行脚本的目录中有其他同名文件)或产生令人困惑的错误消息。所以你可能更喜欢: cd directory && cp file1 file2 destination 仅当第一个命令成功时才会&&运行第二个命令。请注意,这仍然不会撤消运行第一个命令的效果。在整个事情运行之后,你仍然会在,除非你在一个子shell中运行它。directory( ) sudodus 2020-01-28T07:59:52+08:002020-01-28T07:59:52+08:00 编辑 1:修改问题后,第一个替代方案不再有效,第二个替代方案被修改为 'file1 file2' --> 'foo bar' 并扩展了额外的示例。 它对我sh有用 cp directory/file[12] destination 它也应该对你有用。 更高级的替代方法是 for 循环 for i in foo bar;do cp directory/"$i" destination;done 这很容易扩展,例如 for i in foo bar more files;do cp directory/"$i" destination;done 或者 for i in dir1/foo* /dir2/bar*;do cp "$i" destination;done 或者 for i in dir1/* /dir2/*;do cp "$i" destination;done cp编辑 2:值得学习和开始使用,而不是使用高级构造rsync,这是一个具有许多内置功能的工具,可以在本地或通过网络复制并指定要复制的内容。 看man rsync
正如您所观察到的,并非所有 shell 都支持大括号扩展。
方式一:两条独立的路径(即两次写前缀)
如果您的目标真的只是在一个命令中完成,那么一种简单的方法是:
该
cp
命令支持多个源参数,后跟一个目标参数。事实上,这就是在 Bash 中为您工作的命令所发生的事情。Bash 执行大括号扩展,它将您一直使用的命令转换为上面显示的命令。也就是说,当您cp directory/{file1,file2} destination
在 Bash 中运行时,该cp
命令永远不会看到任何花括号。这样的命令简单易懂。我建议考虑一下。
方式2:前缀的变量
但是,如果您的目标是避免重复自己,那么您可能不想那样做,尤其是如果
directory
实际上是一条很长的路。毕竟,如果您必须稍后在脚本中编辑路径,则必须对这两个事件进行更改。您可以将
directory
(或任何可能更长的路径)存储在shell 变量中:$dir
是参数扩展。shell 将其替换为变量中保存的值dir
。使用参数扩展时,您不应省略双引号——除非在极少数情况下您希望执行字段拆分和通配。(这也适用于 Bash。)这是两个命令,但如果您的实际目标是避免重复自己,那可能就是您想要的。你可以写
使其成为“一个命令”,但这并不短,而且可读性也差一些。
(请注意,您不能简单地删除
;
那里。这样做的结果将是一个格式正确的命令,但它不会执行您想要的操作。它会运行cp
命令,将一个使用该值调用的环境变量传递到命令的环境中. 由于扩展的是shell而不是命令,因此这是行不通的。)dir
directory
cp
cp
dir
方式3:
cd
ig到前缀如果您的脚本在执行此操作后没有做任何其他事情,或者当前目录对于后续命令无关紧要,那么如果
destination
是绝对路径,您可以使用:但我不建议将其用于脚本,除非您的脚本执行从该目录运行受益的后续操作,或者当它们从该目录运行时可以更简洁或简单地编写。除非运行脚本的目录对脚本的用途有意义,否则依赖它会使脚本更难推理;此外,如果
cd
命令成功但cp
命令失败,则生成的错误消息可能无法完全说明。相反,我提到了cd
ing 到源目录的技术,因为它是交互式shell 使用的有用模式。(正如Hannu 指出的那样,您可以将任意数量的命令放在一起以在
(
)
subshell中运行它们,并且对 shell 环境的更改,包括 的效果cd
,在命令运行后不会持续存在。这是一种广泛有用的技术,我没有提到它是一个重大的遗漏。但是请注意,这并不能减轻与为 . 的相对路径相关的任何问题。destination
)请注意,如果
cd
命令失败,其他命令通常仍会运行。这可能会复制您不想复制的不同文件(如果在运行脚本的目录中有其他同名文件)或产生令人困惑的错误消息。所以你可能更喜欢:仅当第一个命令成功时才会
&&
运行第二个命令。请注意,这仍然不会撤消运行第一个命令的效果。在整个事情运行之后,你仍然会在,除非你在一个子shell中运行它。directory
(
)
编辑 1:修改问题后,第一个替代方案不再有效,第二个替代方案被修改为 'file1 file2' --> 'foo bar' 并扩展了额外的示例。
它对我sh
有用它也应该对你有用。更高级的替代方法是 for 循环
这很容易扩展,例如
或者
或者
cp
编辑 2:值得学习和开始使用,而不是使用高级构造rsync
,这是一个具有许多内置功能的工具,可以在本地或通过网络复制并指定要复制的内容。看
man rsync