BASH ERROR:语法错误:操作数预期(错误标记为“)

时间:2016-08-17 13:46:39

标签: bash date math operands

我是bash脚本的新手,我的一个脚本存在问题。我正在尝试从一个充满XML文件的文件夹中读取他们的出生日期并计算他们的年龄后,编写一份25岁以下的司机列表。一旦我确定它们不到25,驱动程序数据的文件名就会保存到文本文件中。脚本正在运行直到某个点然后它停止。我得到的错误是:

gdate: extra operand ‘+%s’
Try 'gdate --help' for more information.
DriversUnder25.sh: line 24: ( 1471392000 -  )/60/60/24 : syntax error: operand expected (error token is ")/60/60/24 ")

这是我的代码:

#!/bin/bash

# define directory to search and current date
DIRECTORY="/*.xml"
CURRENT_DATE=$(date '+%Y%m%d')

# loop over files in a directory
for FILE in $DIRECTORY;
do
  # grab user's birth date from XML file
  BIRTH_DATE=$(sed -n '/Birthdate/{s/.*<Birthdate>//;s/<\/Birthdate.*//;p;}' $FILE)

  # calculate the difference between the current date
  # and the user's birth date (seconds)
  DIFFERENCE=$(( ( $(gdate -ud $CURRENT_DATE +'%s') - $(gdate -ud $BIRTH_DATE +'%s') )/60/60/24 ))

  # calculate the number of years between
  # the current date and the user's birth date
  YEARS=$(($DIFFERENCE / 365))

  # if the user is under 25
  if [ "$YEARS" -le 25 ]; then
    # save file name only
    FILENAME=`basename $FILE`
    # output filename to text file
    echo $FILENAME >> DriversUnder25.txt
  fi
done

我不确定为什么它正确输出前10个文件名然后停止。任何想法为什么会这样?

3 个答案:

答案 0 :(得分:3)

您需要引用$BIRTH_DATE的扩展,以防止在值中的空白处进行单词拆分。 (最好引用所有你的参数扩展,除非你有充分的理由不这样做。)

DIFFERENCE=$(( ( $(gdate -ud "$CURRENT_DATE" +'%s') - $(gdate -ud "$BIRTH_DATE" +'%s') )/60/60/24 ))

(根据您的评论,这可能至少允许gdate为您提供更好的错误消息。)

答案 1 :(得分:1)

最佳实践实现看起来像这样:

directory=/ # patch as appropriate
current_date_unix=$(date +%s)

for file in "$directory"/*.xml; do
    while IFS= read -r birth_date; do
        birth_date_unix=$(gdate -ud "$birth_date" +'%s')
        difference=$(( ( current_date_unix - birth_date_unix ) / 60 / 60 / 24 ))
        years=$(( difference / 365 ))
        if (( years < 25 )); then
            echo "${file%.*}"
        fi
    done < <(xmlstarlet sel -t -m '//Birthdate' -v . -n <"$file")
done >DriversUnder25.txt

如果此脚本需要可以使用未安装xmlstarlet的人员,则可以生成XSLT模板,然后使用xsltproc(可在开箱即用的情况下使用)现代操作系统)。

也就是说,如果你运行一次,并将其输出与脚本捆绑在一起:

xmlstarlet sel -C -t -m '//Birthdate' -v . -n  >get-birthdays.xslt

...然后可以修改脚本以将xmlstarlet替换为:

xsltproc get-birthdays.xslt - <"$file"

注意:

  • 正在使用an actual XML parser读取XML输入文件。
  • 扩展for file in "$directory"/*.xml时,会引用扩展但不包含glob(因此允许脚本对名称中包含空格,glob字符等的目录进行操作)。
  • 输出文件一次,为循环打开,而不是每行输出一次(减少不必要地打开和关闭文件的开销)。
  • 使用小写变量名来符合POSIX conventions(指定对操作系统和shell有意义的变量具有全大写的名称,并且具有至少一个较低名称的名称集合-case字符保留供应用程序使用;而有问题的文档与环境变量有关,shell变量共享一个命名空间,使约定相关)。

答案 2 :(得分:0)

问题是某些文件中有多个驱动程序,因此将多个出生日期导入到同一个字符串中。我的解决方案如下:

#!/bin/bash

# define directory to search and current date
DIRECTORY="/*.xml"
CURRENT_DATE=$(date '+%Y%m%d')

# loop over files in a directory
for FILE in $DIRECTORY;
do
  # set flag for output to false initially
  FLAG=false

  # grab user's birth date from XML file
  BIRTH_DATE=$(sed -n '/Birthdate/{s/.*<Birthdate>//;s/<\/Birthdate.*//;p;}' $FILE)

  # loop through birth dates in file (there can be multiple drivers)
  for BIRTHDAY in $BIRTH_DATE;
  do
    # calculate the difference between the current date
    # and the user's birth date (seconds)
    DIFFERENCE=$(( ( $(gdate -ud $CURRENT_DATE +'%s') - $(gdate -ud $BIRTHDAY +'%s') )/60/60/24))

    # calculate the number of years between
    # the current date and the user's birth date
    YEARS=$(($DIFFERENCE / 365))

    # if the user is under 25
    if [ "$YEARS" -le 25 ]; then
      # save file name only
      FILENAME=`basename $FILE`
      # set flag to true (driver is under 25 years of age)
      FLAG=true
    fi
  done

  # if there is a driver under 25 in the file
  if $FLAG == true; then
    # output filename to text file
    echo $FILENAME >> DriversUnder25.txt
  fi
done