【问题标题】:Script is re-reading arguments脚本正在重新读取参数
【发布时间】:2010-09-24 14:29:14
【问题描述】:

当我为脚本提供参数:hi[123].txt 时,它会完全按照我的意愿行事。 但是如果我指定通配符( hi*.txt ),它将重新读取一些文件。

我想知道如何修改这个脚本来解决这个愚蠢的问题:

#!/bin/sh

count="0"
total="0"
FILE="$1"  #FILE specification is now $1 Specification..

for FILE in $@
do
  #if the file is not readable then say so
     if [ ! -r $FILE ];
         then
         echo "File: $FILE not readable"
     exit 0
     fi


# Start processing readable files
  while read line
          do

             if [[ "$line" =~ ^Total ]];
                 then
                   tmp=$(echo $line | cut -d':' -f2)

                   total=$(expr $total + $tmp)

                   echo "$FILE (s) have a total of:$tmp "
                   count=$(expr $count + 1)

             fi

        done < $FILE
done
echo " Total is: $total"
echo " Number of files read is:$count"

【问题讨论】:

  • 在第一个 if 块之后,添加:echo $FILE。是否有重复的文件正在打印?
  • 删除大部分代码并将其简化为最简单的循环。就像 strager 说的那样,打印出来。从小处着手,然后发展壮大。
  • 我会第二个(或第三个)strager 的调试建议。
  • 这与@lampshade 昨天提出的stackoverflow.com/questions/326942/… 非常接近。我很想将其作为重复项关闭 - 尽管问题略有不同。
  • 似乎@lampshade 还没有阅读上一个问题的答案 - 诸如“使用“$@”而不是 $* 或 $@”之类的点还没有被听过。另外,尾巴y'day's question 谈到了重读文件。

标签: linux bash shell scripting


【解决方案1】:

我不知道有什么问题,但我注意到了一点:

for FILE in $@ 更改为 for FILE in "$@" 。因为如果文件有嵌入的空格,你现在就安全了。然后它将扩展为"$1" "$2" ...,而不是$1 $2 ...(请注意您使用$FILE 的任何地方也记得"" 它)。

和别人说的一样,进入循环前不需要初始化FILE。它将自动设置为 for 循环中扩展位置参数的每个文件名。

但是,我会使用这样的 awk 脚本:

awk -F: '
/^Total/ { 
    total += $2
    # count++ not needed. see below
    print FILENAME "(s) have a total of: " $2
} 

END { 
    print "Total is: " total
    print "Number of files read is: " (ARGC-1) 
}' foo*.txt

请注意,当一个文件包含多个“^Count”行时,如果您依靠count 告诉您读取的文件数,您确实会说您读取的文件比实际读取的多。

【讨论】:

  • 第二点,在处理文件时,您几乎总是希望使用“$@”而不是替代选项。
【解决方案2】:

这个解决方案怎么样:

for FILE in `/bin/ls $@`
do
. . .

这将有效地消除重复,因为/bin/ls hi1.txt hi1.txt hi1.txt 应该只显示一次hi1.txt

虽然我不确定它为什么要重新读取文件。通配符扩展应该只包含每个文件一次。您是否有一些与hi*.txt 匹配的文件是指向与hi[123].txt 匹配的文件的链接?

【讨论】:

  • 当一个文件中有空格或者有一个名为-l的文件时会发生什么?
  • 使用 ls 来迭代这样的文件不是一个好的选择。最好使用“for FILE in *”(假设您想要 CWD 中的文件),因为即使文件名中有空格它也可以工作。
【解决方案3】:

这似乎是多余的:

FILE="$1"  #FILE specification is now $1 Specification..

for FILE in $@
  ...

初始分配会立即被覆盖。

总的来说,这似乎是一个更适合像 awk 或 perl 这样的行处理语言的任务。

考虑一下这个 awk 脚本的内容:

BEGIN{
   TOTAL=0;
   COUNT=0;
   FS=':';
}
/^Total/{
   TOTAL += $2;
   COUNT++;
   printf("File '%s' has a total of %i",FILENAME,TOTAL);
}
END{
   printf("Total is %i",TOTAL);
   printf("Number of files read is%i",COUNT);
}

【讨论】:

  • 哎呀,我花了很长时间才完成我的脚本,只是为了找出如何获取读取的文件数量:) 我想我会想出第一个,但你更快。竖起大拇指。我们的解决方案看起来完全一样。如果一个文件中有>1个“^Total”,那么您的文件数将是错误的:)
  • 哦,nvm,OP 做了同样的事情 :) 哈哈也许 是他的错误。大声笑
  • 我认为您的方法可能是正确的,但我只是写了我的方法以符合我从 OP 中读到的意图。嘿!我看到“(ARGC -1)”。不错。
【解决方案4】:

出错时,以非零状态退出。同样在错误时,将错误报告给标准错误,而不是标准输出——尽管这对你来说可能有点高级。

echo "$0: file $FILE not readable" 1>&2

理论上 1 是不必要的(尽管我记得如果省略它,Windows 上的 shell 实现会出现问题)。在错误消息的开头回显脚本名称“$0”也是一个好主意 - 当您的脚本在其他上下文中使用时,它使错误跟踪更容易。

我相信这个 Perl 单行代码可以完成您所追求的工作。

perl -na -F: -e '$sum += $F[1] if m/^Total:/; END { print $sum; }' "$@"

我知道您正在学习 shell 编程,但是 shell 编程的重要一件事是知道要使用哪些程序。

【讨论】:

    猜你喜欢
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    • 2013-10-19
    • 2013-12-27
    • 2018-01-01
    相关资源
    最近更新 更多