1

我正在尝试编写一个非常简单的 bash 脚本来修改许多文件,并将每个命令的结果输出到日志中,以检查命令是否成功完成。一切似乎都在工作,除了我无法将带有变量的 CAT 传递给我的脚本——我不断收到cat: >>: No such file or directory错误消息。

#! /bin/bash

file1="./file1"
file2="./file2"

check () {
  if ( $1 > /dev/null ) then
    echo "     $1 : completed" | tee -a log
    return 0;
  else
    echo "ERR> $1 : command failed" | tee -a log
    return 1;
  fi
}

check "cp $file1 $file1.bak"            # this works fine
check "sed -i s/text/newtext/g $file1" # this works, too
check "cat $file1 >> $file2"          # this does not work

我尝试了任意数量的引用命令的组合。我可以让它工作的唯一方法是使用以下内容:

check $(cat $file1 >> $file2)

但是,这不会将命令本身check仅传递给返回值,因此$1在函数中check进行/dev/null而不是执行的命令,这不是我想要的特定行为。

为了完整起见,该log文件如下所示:

     cp ./file1 ./file1.bak : completed
     sed -i s/text/newtext/g ./file1 : completed
ERR> cat ./file1 >> ./file2 : command failed

我确信解决方案相当简单,但它已经让我躲了几个小时,并且没有多少谷歌搜索产生任何帮助。谢谢你看。

4

2 回答 2

0

问题是cat命令中的 I/O 重定向没有被解释为 I/O 重定向,而是作为cat命令的一个简单参数。cat引起悲伤的并不是I/O 重定向。尝试管道也会给您带来问题。

可用于解决此问题的选项包括:

check "cp $file1 $file2" # Use copy instead of cat and I/O redirection; clobbers file2

check "eval cat $file1 >> $file2" # Use eval to handle I/O redirection, piping, etc

如果其中一个$file1$file2包含 shell 特殊字符,则该eval选项很危险。

cp命令替代了无需 I/O 重定向即可工作的东西。您甚至可以使用(微观)shell 脚本来处理这项工作——您的脚本执行 shell 脚本,而 shell 脚本处理重定向:

#!/bin/sh
exec cat ${1:?} >> ${2:?}

如果缺少参数 1 或 2(但不反对额外参数),这会生成默认错误消息。

于 2013-08-18T22:42:00.037 回答
0

编辑:我在下面第一次尝试的方法不太奏效。即使不使用 bash 魔法,还有另一个技巧可以解决这个问题,但它变得越来越丑陋。


在这种>>情况下,重定向发生在错误的级别。你最终要求cat读取./file,然后是一个名为的文件>>,然后是./file2. 要使重定向发生,您需要在其他地方进行(见下文),或调用eval.

我建议不要使用eval,而是重新调整函数的逻辑checkcheck您可以在顶层重定向,例如:

check() {
    if "$@"; then
        echo "     $@ : completed" | tee -a log
        return 0
    fi
    echo "ERR> $@ : failed, status $?" | tee -a log
    return 1
}

check cp "$file1" "$file.bak"                     # doesn't print anything
check sed -i s/text/newtext/g "$file1" >/dev/null # does print, so >/dev/null
check cat "$file1" >> "$file2"

(此处调用中的双引号check以防万一file1和/或file2曾经获得元字符,如*or;或空格等)

编辑:正如@cdm 和@rici 所指出的,对于附加到文件的情况,这会失败,因为check即使对于tee命令,它的输出也会被重定向。重定向再次发生在错误的级别。可以通过添加另一个间接级别来解决这个问题:

append_to_file() {
    local fname
    fname="$1"
    shift
    "$@" >> "$fname"
}

check cp "$file1" "$file.bak"
check append_to_file /dev/null sed -e s/text/newtext/g "$file1"
check append_to_file "$file2" cat "$file1"

但是,现在,已完成和失败的消息记录append_to_file在前面,这真的很笨拙。我想我会回去eval

于 2013-08-18T22:51:26.777 回答