Bash脚本错误:我在for循环中给出'没有这样的文件或目录'

时间:2014-02-08 00:49:35

标签: bash shell

长话短说,我需要编写一个shell脚本。该脚本将采用单个命令行参数,该参数将是目录路径。 然后,该脚本将读取该目录中的每个文件并将其输出到标准输出;输出将以HTML格式显示。

文件将采用以下格式:

owner sysadmin group
admin ajr
loc S-309
ser 18r97
comment noisy fan

到目前为止我所拥有的:

PATH=/bin:/usr/bin

cd "$@"
if [ test $? -ne 0]
then 
    exit 1
fi

filenames=$(ls "$@")
for i in $filenames
do
    while read item value
    do
        if [ $item="owner" ] || [ $item="admin" ] || [ $item="loc" ] || [ $item="ser"]
        then
            a[$item]=$value
        fi
    done < i
done

echo '<html>'
echo '<body>'
echo '<table border=1>'
echo '<tr><th>hostname</th><th>location</th><th>Admin</th><th>Serial Number</th><th>owner</th><tr>'
for i in filename
do
    echo '<tr><td>'$i'</td><td>'${i[loc]}'</td><td>${i[admin]}'</td><td>'${i[ser]}'</td><td>'${i[owner]}'</td><tr>'
done
echo 
echo '</table>'
echo '</body>'
echo '</html>'

HTML不是我主要关注的问题,因为我只是遵循给定的格式,每个值介于两者之间。但是,我收到一个错误,我不明白为什么:

invrep: line 10: i: no such file or directory

但是我在循环中使用它。为什么它会给我这个错误?

另外要确认,我使用的目录存在;我不确定这是否与任何事情有关。

2 个答案:

答案 0 :(得分:3)

警告Lector :问题中的代码已被编辑。我评论过的代码可能不是你能看到的代码。


不直接问题(chepner在他的comment中)被诊断出来,但是:

cd "$@"
if [ test $? -ne 0]
then 
    exit 1
fi

有各种各样的问题。您没有验证只有一个参数,并且您传递了给cd的所有参数,这些参数可能只是静静地忽略了剩余。 test行应使用[test,但不能同时使用两者。如果您使用[,则最后一个参数必须为],因此您错过了一个空格:

if test $? -ne 0
if [ $? -ne 0 ]

但是,您可以通过以下方式缩短该段:

cd "$@" || exit 1

(或者你可以放弃1,尽管我会留在那里)。

您可能需要考虑:

case $# in
1) cd "$1" || exit 1;;
*) echo "Usage: $0 directory" >&2; exit 1;;
esac

这将验证是否传递了一个参数,并且它命名了一个可以cd到的目录。

您的循环代码也存在问题。一旦你解决了问题,while循环应该从"$i"重定向:

filenames=$(ls "$@")
for i in $filenames
do
    while read item value
    do
        if [ $item="owner" ] || [ $item="admin" ] || [ $item="loc" ] || [ $item="ser"]
        then
            a[$item]=$value
        fi
    done < $i
    # Print HTML here!! Not after this loop
done

你的HTML循环也有很多问题 - 特别是使用$i作为数组而不是$a

PATH=/bin:/usr/bin

case $# in
1) cd "$1" || exit 1;;
*) echo "Usage: $0 directory" >&2; exit 1;;
esac

echo '<html>'
echo '<body>'
echo '<table border=1>'
echo '<tr><th>hostname</th><th>location</th><th>Admin</th><th>Serial Number</th><th>owner</th><tr>'

filenames=$(ls "$@")
for i in $filenames
do
    while read item value
    do
        if [ $item="owner" ] || [ $item="admin" ] || [ $item="loc" ] || [ $item="ser"]
        then
            a[$item]=$value
        fi
    done < $i
    echo "<tr><td>$i</td><td>${a[loc]}</td><td>${a[admin]}</td><td>${a[ser]}</td><td>${a[owner]}</td><tr>"
done

echo '</table>'
echo '</body>'
echo '</html>'

这仍然无法解决使用ls生成文件名列表的问题。为此,给定脚本的其余部分,完全丢失filenames并使用for file in *代替。然后,您还需要在I / O重定向中引用$i

PATH=/bin:/usr/bin

case $# in
1) cd "$1" || exit 1;;
*) echo "Usage: $0 directory" >&2; exit 1;;
esac

echo '<html>'
echo '<body>'
echo '<table border=1>'
echo '<tr><th>hostname</th><th>location</th><th>Admin</th><th>Serial Number</th><th>owner</th><tr>'

for i in *
do
    while read item value
    do
        if [ $item = "owner" ] || [ $item = "admin" ] ||
           [ $item = "loc" ]   || [ $item="ser"]
        then
            a[$item]=$value
        fi
    done < "$i"
    echo "<tr><td>$i</td><td>${a[loc]}</td><td>${a[admin]}</td><td>${a[ser]}</td><td>${a[owner]}</td><tr>"
done

echo '</table>'
echo '</body>'
echo '</html>'

(在循环中if语句中也修复了间距。代码仍然不是很优雅,但它与原始代码有些相关。)

答案 1 :(得分:2)

filenames=$(ls "$@")

是错误的,任何人都不应该使用。请参阅http://mywiki.wooledge.org/BashPitfalls中的第一个条目,或整个页面http://mywiki.wooledge.org/ParsingLs

如果您的参数列表是一组目录,则内部循环看起来更像是这样:

declare -A a
for dir in "$@"; do
  for i in "$dir/"*; do
    while read -r item value; do
      case $item in
        owner|admin|loc|ser)
          a[$item]=$value
          ;;
       esac
     done <"$i"
  done
done