为什么Linux命令CP在CLI和脚本中的行为有所不同?

时间:2015-04-16 10:05:31

标签: linux bash cp

我想复制一堆Verilog / systemverilog源代码,所以我使用CP和通配符表达式:

cp <some_dir>/*.{v,sv,svh} .

有效。但是,当我将它放到具有完全相同行的脚本时,CP命令将失败并显示日志:

cp: cannot stat `../../mytest/spiTest/*.{v,sv,svh}': No such file or directory

这是怎么回事?

PS:我使用bash作为shell。


这是我的剧本:

#!/bin/bash
rdir=../../mytest/spiTest
f1="$rdir/bench.lst"
f2="$rdir/cphex" #the script to copy rom data
f3="$rdir/make*" #makefile scripts
f4="$rdir/*.hex" #rom files
f5="$rdir/*.{v,sv,svh}" #testbench files
echo 'Copying files...'
cp $f1 $f2 $f3 $f4 .
cp $f5 .

我确实将第一行改为

#!/bin/bash -vx

再次运行此脚本,我得到:

#!/bin/bash -vx

rdir=../../mytest/spiTest
+ rdir=../../mytest/spiTest
f1="$rdir/bench.lst"
+ f1=../../mytest/spiTest/bench.lst
f2="$rdir/cphex" #the script to copy rom data
+ f2=../../mytest/spiTest/cphex
f3="$rdir/make*" #makefile scripts
+ f3='../../mytest/spiTest/make*'
f4="$rdir/*.hex" #rom files
+ f4='../../mytest/spiTest/*.hex'
f5="$rdir/*.{v,sv,svh}" #testbench files
+ f5='../../mytest/spiTest/*.{v,sv,svh}'

echo 'Copying files...'
+ echo 'Copying files...'
Copying files...
cp $f1 $f2 $f3 $f4 .
+ cp ../../mytest/spiTest/bench.lst ../../mytest/spiTest/cphex ../../mytest/spiTest/makefile ../../mytest/spiTest/makefile.defines ../../mytest/spiTest/rom.hex ../../mytest/spiTest/rom_if.hex .
cp $f5 .
+ cp '../../mytest/spiTest/*.{v,sv,svh}' .
cp: cannot stat `../../mytest/spiTest/*.{v,sv,svh}': No such file or directory

3 个答案:

答案 0 :(得分:4)

检查脚本的第一行。它可能是:

#!/bin/sh

将shell从BASH切换到Bourne Shell。使用

#!/bin/bash

代替。

[编辑] 您遇到了扩展问题。 BASH有一定的顺序,可以扩展模式和变量。这意味着:

f5="$rdir/*.{v,sv,svh}" #testbench files

被引用,因此此时不会发生文件名扩展。仅展开变量$rdir。当

cp $f5 .

执行后,BASH首先查找要扩展的文件名,但没有。然后它展开变量(f5),然后使用两个参数调用cp../../mytest/spiTest/*.{v,sv,svh}.。由于cp期望shell已经执行了文件名扩展,因此会出现错误。

要解决此问题,您必须使用数组:

f5=($rdir/*.{v,sv,svh})

这将替换变量,然后展开文件名并将所有内容放入数组f5中。然后,您可以使用此数组调用cp,同时保留空格:

cp "${f5[@]}" .

这里的每个角色都很重要。 [@]告诉BASH在这里扩展整个数组。引述说:保留空白。 {}必须告诉BASH [@]是要扩展的变量“name”的一部分。

答案 1 :(得分:4)

问题在于:替换的顺序。 Bash在变量扩展之前执行大括号扩展。在第cp $f5 .行中,bash将执行:

  1. 支撑扩张:不适用
    • 这是关键点:变量包含一个大括号表达式,但是shell需要才能看到它。
  2. 代字号扩展:不适用
  3. 参数展开:是 - cp ../../mytest/spiTest/*.{v,sv,svh} .
  4. 命令替换:不适用
  5. 算术扩展:不适用
  6. 流程替换:不适用
  7. 分词:不适用
  8. 文件名扩展:是的,bash查找该目录中以字符串
    结尾的文件 .{v,sv,svh}。它找不到,nullglob未设置,因此模式不会从命令
  9. 中删除
  10. 引用删除:不适用
  11. 现在命令已执行并因您看到的错误而失败。

    https://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions

    解决方案:

    1. 使用Aaron的数组想法
    2. (不推荐)强制进行第二轮扩展:eval cp $f5 .

答案 2 :(得分:1)

该行

 f5="$rdir/*.{v,sv,svh}" #testbench files

可能是错的。首先,避免在行尾注释,它们应该(至少为了可读性)在一个单独的行中。然后,避免在变量赋值中使用globbing。因此,请删除该行,然后再编码(即将旧的cp $f5 .行替换为

 cp "$rdir"/*.{v,sv,svh} .
顺便说一句,我会测试"$rdir"确实是一个带

的目录
 if [ ! -d "$rdir" ] ; then
    echo invalid directory $rdir > /dev/stderr
    exit 1
 fi

您应该阅读Advanced Bash Scripting Guide