Bash脚本:意外令牌“完成”附近的语法错误

时间:2016-07-16 08:54:51

标签: bash shell if-statement for-loop scripting

我是bash脚本的新手,我必须创建这个脚本,它将3个目录作为参数,并在第三个目录中复制第一个中不在第二个目录中的所有文件。

我是这样做的:

#!/bin/bash

if [ -d $1 && -d $2 && -d $3 ]; then
    for FILE in [ ls $1 ]; do
        if ! [ find $2 -name $FILE ]; then
            cp $FILE $3
    done
else echo "Error: one or more directories are not present"
fi

我尝试执行它时得到的错误是:“第7行:意外令牌'完成'附近的语法错误” 我真的不知道如何让它发挥作用! 即使我正在使用#!/ bin / bash,我仍然必须在尝试执行时显式调用bash,否则它表示不允许执行,任何人都知道为什么? 在此先感谢:)

4 个答案:

答案 0 :(得分:3)

一些建议:

  1. 无害双重引用变量

    cp "$FILE" "$3" # prevents wordsplitting, helps you filenames with spaces
    
  2. for语句失败的根本原因是 - 语法 - 应该是:

    for FILE in ls "$1";
    
  3. 但是,从不解析ls输出。检查[ this ]

     for FILE in ls "$1"; #drastic
    
  4. 而不是第2步中的for-loop使用find-while-read组合:

    find "$1" -type f -print0 | while read -rd'' filename #-type f for files
    do
    #something with $filename
    done
    
  5. 为脚本使用小写变量名,因为大写变量是为系统保留的。检查[this]
  6. 使用[ shellcheck ]等工具来提高脚本质量。
  7. 修改

    由于您已经提到输入目录只包含文件,我的替代方法是

    [[ -d "$1" && -d "$2" && -d "$3" ]] && for filename in "$1"/*
    do
        [ ! -e "$2/${filename##*/}" ] && cp "$filename" "$3"
    done
    

    如果您对${filename##*/}检查[ shell parameter expansion ]感到困惑。

    Sidenote :在linux中,虽然不鼓励使用file name之类的非标准文件名,但这种情况并不少见。 礼貌@chepner& @mklement0他们的评论极大地改善了这个答案:)

答案 1 :(得分:1)

你的剧本:

if ...; then
  for ...; do
    if ...; then
      ...
  done
else
  ...
fi

固定结构:

if ...; then
  for ...; do
    if ...; then
      ...
    fi       # <-- missing
  done
else
  ...
fi

如果您希望脚本可执行,请将其设为:

$ chmod +x script.sh

请注意,您的脚本中还有其他问题。最好写成

dir1="$1"
dir2="$2"
dir3="$3"

for f in "$dir1"/*; do
  if [ ! -f "$dir2/$(basename "$f")" ]; then
    cp "$f" "$dir3"
  fi
done

答案 2 :(得分:1)

这不完全正确:

for FILE in $(ls $1); do 
    < whatever you do here >
done

如果在该文件夹中存在如下文件名,则该循环存在一个大问题:'我是一个带有spaces.txt的文件名'。 而不是那个循环尝试这个:

for FILE in "$1"/*; do
    echo "$FILE"
done

此外,您必须使用 fi 关闭每个 if 语句。

另一件事,如果您使用BASH(#!/ usr / bin / env bash ),强烈建议您在测试条件中使用双括号:

if [[ test ]]; then
    ...
fi

例如:

$ a='foo bar'
$ if [[ $a == 'foo bar' ]]; then
> echo "it's ok"
> fi
it's ok

但是,这个:

$ if [ $a == 'foo bar' ]; then
> echo "it's ok"; 
> fi
bash: [: too many arguments

答案 3 :(得分:-1)

你忘记了最fi后的if

此外,方括号和find都不会以这种方式工作。这个脚本(就像现在一样)用于我的PC:

#!/bin/bash

if [[ -d "$1" && -d "$2" && -d "$3" ]] ; then
    ls -1 "$1" | while read FILE ; do
        ls "$2/$FILE" >/dev/null 2>&1 || cp "$1/$FILE" "$3"
    done
else echo "Error: one or more directories are not present"
fi

请注意,在单次运行后,当$ 2和$ 3引用不同的目录时,这些文件仍然不在$ 2中,因此下次运行脚本时,它们将再次被复制,尽管它们已经存在于$ 3中。 / p>