数据库插入语句的 Bash 脚本 TUI 不起作用。设置 bash 内置错误消息

数据库插入语句的 Bash 脚本 TUI 不起作用。设置 bash 内置错误消息

我有以下 bash 脚本,它提供了 Zentiy TUI 将数据插入数据库。

#!/bin/bash

    tip_run1="$(zenity --entry --text "ENTER number of tip runs:" --entry-text "1")"
     a=1

until
        [[ $tip_run1 -lt $a ]]
do
        input="$(zenity --forms --title="table tip_run" --text="Add a new tip run" --separator="," \
        --add-entry="ENTER start_time, e.g. 8:20: " \
        --add-entry="ENTER finish_time, e.g. 12:30: " \
        --add-entry="ENTER weight in kg, -t numeric: "\
    --add-entry="ENTER a note, -t text: ")"

psql -tA -U chh1 -d crewdb -c "SELECT SETVAL('tip_run_tip_runid_seq', (SELECT MAX(tip_runid) FROM tip_run), true);" >/dev/null 2>&1

startt="$(echo "$input" | awk -F, -v  OFS=, '{print $1}')"
finisht="$(echo "$input" | awk -F, -v  OFS=, '{print $2}')"

st="$( date --date="$startt" +%s  2>/dev/null )"
ft="$( date --date="$finisht" +%s 2>/dev/null )" 

if [ -n "$st" -a "$ft" ] ; then
    
    startt="$(date +%H:%M  -d "$startt"  )"
    finisht="$(date +%H:%M -d "$finisht" )"
    tzdiff="$(( ft - st ))"
else
    tzdiff=0
fi    

while [[  ( ( ! "$startt"   =~ ^[0-1][0-9]:[0-5][0-9]$ ) && ( ! "$startt"  =~ ^[0-2][0-3]:[0-5][0-9]$ ) ) || 
          ( ( ! "$finisht"  =~ ^[0-1][0-9]:[0-5][0-9]$ ) && ( ! "$finisht" =~ ^[0-2][0-3]:[0-5][0-9]$ ) ) || 
          ( "$tzdiff" -le 0 )  ]];
do
    var2="$(zenity --forms --title="start_time and/or finish_time are incorrect" --text "Add a start_time and a finish_time"  --separator="," \
                   --add-entry="WARNING! Something went wrong. Please enter a valid start_time, e.g. 8:20: " \
                   --add-entry="WARNING! Something went wrong. Please enter a valid finish_time, e.g. 12:30: ")"
    tzdiff=0
    
    if [ -n "$var2" ] ; then
       b1=$(echo "$var2" | cut -d, -f1 )
       b2=$(echo "$var2" | cut -d, -f2 )
       
       if [ -n "$b1" -a -n "$b2"  ] ; then
           tz1=$( date --date="$b1" +%s 2>/dev/null )
           tz2=$( date --date="$b2" +%s 2>/dev/null )
           
       if [ -n "$tz1" -a -n "$tz2" ] ; then
              startt=$(date +%H:%M -d "$b1" )
              finisht=$(date +%H:%M -d "$b2" )
              tzdiff=$(( tz2 - tz1 ))
           fi
       fi
    fi
done

var2="$startt,$finisht"

input="$( echo "$input" | awk -v vart="$var2" 'BEGIN {  FS="," } { print vart "," $3 "," $4 ; }' )"

input="$((IFS=, read -r  b c d e ; echo "${b}ttt,${c}ttt,${d}xxx,${e}www" )<<<"$input")"

IFS=,; set -f; set --$input; out=
for i in "$@"; do

        case "$i" in
                xxx) var2="$(zenity --forms --title="weight field in table tip_run" --text "Add a weight in kg"  --separator="," \
                                --add-entry="WARNING! You forgot to enter a weight. Please enter a valid weight, e.g. 12.5: ")"

                                until [[ ${var2} =~ ^[0-9]+([.][0-9]+)?$ ]] || [[ ${var2} = NULL ]]; do

                                        var2="$(zenity --forms --title="weight field in table tip_run" --text "Add a weight in kg"  --separator="," \
                                        --add-entry="WARNING! You either forgot to enter a weight or didn't enter a number. Please enter a valid weight, e.g. 12.5: ")"

                                done

                                out="$out,${var2}"                          
                                ;;
                
        NULLxxx) out="$out,${i/%xxx/}";;        
                *xxx) if [[ "${i/%xxx}" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
            
            out="$out,${i/%xxx/}"
            
            else
                 until [[ ${var2} =~ ^[0-9]+([.][0-9]+)?$ ]] || [[ ${var2} = NULL ]]; do

                                        var2="$(zenity --forms --title="weight field in table tip_run" --text "Add a weight in kg"  --separator="," \
                                        --add-entry="WARNING! You either forgot to enter a weight or didn't enter a number. Please enter a valid weight, e.g. 12.5: ")"

                                done

                                out="$out,${var2}"
            fi
                                ;;
        *ttt) out="$out,'${i/%ttt/}:00'";;
                #TRUEbool) out="$out,${i/%bool/}";;
                #FALSEbool) out="$out,${i/%bool/}";;
                #*bool) echo "empty input not allowed"; exit 0;;
        NULLwww) out="$out,${i/%www/}";;
        www)            var2="$(zenity --forms --title="note field in table tip_run" --text "Add a note"  --separator="," \
                                --add-entry="WARNING! You either forgot to enter a note. Please enter a note or NULL: ")"

                                until [[ ! ${var2} = ""  ]]; do

                                        var2="$(zenity --forms --title="note field in table tip_run" --text "Add a note"  --separator="," \
                                        --add-entry="WARNING! You either forgot to enter a note or to enter NULL. Please enter a note or NULL: ")"

                                done
                
                if [[ ${var2} = "NULL"  ]]; then
                    out="$out,${var2}"
                else

                out="$out,\$\$${var2}\$\$"
                fi;;

        *www) out="$out,\$\$${i/%www/}\$\$";;
esac;
done

#echo "${out:1}"

echo "" >> /tmp/crew.txt
echo "" >> /tmp/crew.txt
echo "-- INSERT INTO tip_run:"  >> /tmp/crew.txt
echo "INSERT INTO tip_run (date_linkid, start_time, finish_time, weight, note) VALUES (${out:1});" >> /tmp/crew.txt

let a++

done

我知道脚本的各个组件可以工作,但是当我运行它时,我收到以下错误消息:

./tip_run.txt: line 64: set: --: invalid option
set: usage: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]

第 64 行位于以下位置:

 62 input="$((IFS=, read -r  b c d e ; echo "${b}ttt,${c}ttt,${d}xxx,${e}www" )<    <<"$input")"
 63 
 64 IFS=,; set -f; set --$input; out=
 65 for i in "$@"; do
 66 
 67         case "$i" in

我尝试了不同的设置选项,但似乎都没有用。有人能帮我解决这个问题吗?

答案1

问题在于set --$input,它“应该”是set -- $input。或者也许它根本就不应该在这里。


目的set -- $something和工作原理

很少有答案你之前的一个问题建议使用set -- $OUTPUT。完整相关代码片段如下:

IFS=,; set -f; set -- $OUTPUT
for i do …

注意:for i dofor i; dofor i in "$@"; do是等价的。

在那个问题中,你想使用作为分隔符来分割变量的内容,。然后你想循环遍历结果块。你尝试使用awk。据指出,shell 可以做到这一点,而代码片段就是方法。让我们分析一下它究竟是如何工作的。

主要思想是利用未加引号的变量在扩展时进行分词的事实。通常,未加引号的变量也会触发文件名生成(例如,通配符的扩展,就像*它们存储在变量中一样)。通常你既不需要文件名生成也不需要分词,因此一般规则是总是双引号除非你知道自己在做什么。在少数情况下,不引用是合理的set -- $something,但你需要知道你在做什么。这就是你正在做的事情:

  • 您指定IFS=,以便将来的单词分割,作为分隔符;
  • 通过set -f您禁用文件名生成机制。

此后,未加引号的单词$something可以安全地扩展为零个或多个单词。但您仍需要循环遍历它们。循环遍历位置参数很容易(即,您可以使用$1$2、……获取参数;或者使用$@或(不等价)$*一次性获取所有参数;加引号很重要)。

set是 shell 内置命令,可以设置或取消设置 shell 选项(例如set -f我们已经知道的)和位置参数。看起来不像选项的参数会修改位置参数数组。例如,set foo bar使$1展开为foo$2展开为bar

扩展中的单词$something可以用 分配给位置参数,set $something但这至少在两个方面存在缺陷:

  1. 如果某个单词看起来像 的选项set,它将被视为选项。该选项可能会或可能不会被识别为有效(受支持)选项。
  2. 如果$something扩展为恰好零个单词,那么set $something最终将作为唯一set的不会修改位置参数数组。

第一个问题是普遍存在的,并不特定于set。许多工具(包括set)都支持双划线 ( --)来解决它。第二个问题特定于set。该工具的设计--也解决了第二个问题。我的意思是,如果$something扩展为恰好零个单词,则set -- $something最终会变成这样,set --这会按预期修改位置参数数组:所有位置参数都将被取消设置。

因此,这set -- $something是一种强大的方法,可以将扩展中的零个或多个单词分配$something给位置参数,然后您可以轻松地循环。

笔记:

  • 可以跳过set -- $something并开始这样的循环:

    IFS=,; set -f
    for i in $something; do
    

    IFS但是然后你进入了修改后的和的循环set -f。使用set -- $something和然后for i do允许你在之前恢复for。在你当前的代码中,你似乎并不关心修改后的IFSset -f。一般来说,它们以后可能会适得其反(你应该已经知道)。

  • 在 Bash 中,你可以使用命名数组(myarray例如这个答案) 并循环遍历它。


set --$something或有什么问题set --"$something"

如果set --$something(或set --"$something")--与 扩展中的第一个单词连接在一起$something(或 与 扩展中的唯一单词连接在一起"$something")。有些极端情况会使--保留--,但最有可能的是 变成--whatever。此字符串以 开头-,因此看起来像是一个或多个选项。

关于选项的约定很少。大多数工具支持单破折号单字符选项(短选项,例如-a-1)。许多工具支持双破折号多字符选项(长选项,例如--almost-all)。很少有工具将单破折号多字符选项视为单个选项(例如 中7z-bd-bt),但许多工具会将 视为-abc-a -b -c如果 和-a都不-b需要选项参数。通常,工具可以以任何它想要的方式解析和解释其参数,因此这可能很复杂。

set --whateverwhatever视为长选项,它根本不支持长选项。它看起来set --whatever几乎像set -- -w -h -a -t -e -v -e -r,除了它--确实不是就像前面阐述的双破折号一样。这里--尝试作为一个简短的选项,一个单破折号单字符选项,其中单字符部分恰好是破折号。但是没有这样的简短选项:

set: --: invalid option

--需要单独的参数。通过省略空格,--set错误地将其第一个参数解释为选项。相当有趣,关于正确使用 --表示选项的结束。


出什么问题了set -- "$something"

在一条(现已删除)评论中,出现了 的想法set -- "$something",可能是因为“始终引用”规则。这会扩展$something为一个单词。最初的主要目标是拆分为多个单词,因此引用肯定不是正确的做法。

假设有一段时间你真的想要$something一个单词,你做到了,set -- "$something"而且效果很好。在这种情况下,连续性for i in "$@"; do是没有意义的,因为你正在迭代一个单身的元素。无需循环。


您真的需要setforcase吗?

在你的已经提到的问题您使用相同的代码测试了每个字段。迭代字段是一个好主意。

在当前问题中:

  • $input在某些时候,你正在传递值awk,我强烈怀疑这是设计使然确切地之后是四个以逗号分隔的字段。
  • 然后你用IFS=, read -r来拆分$input$b$c和;这些是字段。$d$e
  • 然后您${b}ttt,${c}ttt,${d}xxx,${e}www又可以全部省钱了。
  • 然后你把它分开再次,这次是set -- $input,因此 而不是$b$c$d并且 ,$e您有$1$2$3$4
  • 然后你循环遍历字段,并使用case不同的代码来为不同的字段运行不同的代码。我的意思是NULLxxx只能匹配第三个字段(${d}xxx)。你使用xxx来识别第三个字段,但是$d你之前已经把它分开了。为什么不首先进行测试$d呢?NULL

如果你事先不知道有多少个字段,拆分为位置参数很有用。如果你知道你正好有四个,那么

IFS=, read -r  b c d e

在主 shell 中,将为您提供四个定义明确的单独变量。您仍然可以使用 进行循环for i in "$b" "$c" "$d" "$e"; do,但由于在原始代码中只有${b}ttt${c}ttt共享一个可以匹配的情况(*ttt),因此循环"$b""$c",然后分别解析"$d"和是有意义的。和"$e"通用的代码非常简单,您也可以重复它而不引入循环:"$b""$c"

out="$out,'$b:00'"
out="$out,'$c:00'"

进而:

# what to do if $d is empty
# what to do if $d is NULL
# what to do if $e is NULL
# what to do if $e is empty
# what to do if $e is valid
# etc.

ttt如果您希望能够快速升级代码以支持需要验证的其他字段(如已支持的字段),则使用或标记不同字段的方法xxx是有意义的。是这样吗?即使如此,拆分、连接并再次拆分似乎过于复杂。

答案2

有人能帮我找到正确的方法吗?

与往常一样,对于不太适用的 bash 脚本,我会通过ShellCheck – shell脚本分析工具

您的脚本生成以下错误/警告:

$ shellcheck myscript
 
Line 23:
if [ -n "$st" -a "$ft" ] ; then
              ^-- SC2166: Prefer [ p ] && [ q ] as [ p -a q ] is not well defined.
 
Line 45:
       if [ -n "$b1" -a -n "$b2"  ] ; then
                     ^-- SC2166: Prefer [ p ] && [ q ] as [ p -a q ] is not well defined.
 
Line 49:
       if [ -n "$tz1" -a -n "$tz2" ] ; then
                      ^-- SC2166: Prefer [ p ] && [ q ] as [ p -a q ] is not well defined.
 
Line 62:
input="$((IFS=, read -r  b c d e ; echo "${b}ttt,${c}ttt,${d}xxx,${e}www" )<<<"$input")"
       ^-- SC1102: Shells disambiguate $(( differently or not at all. For $(command substition), add space after $( . For $((arithmetics)), fix parsing errors.
 
Line 64:
IFS=,; set -f; set --$input; out=
                     ^-- SC2086: Double quote to prevent globbing and word splitting.

Did you mean: (apply this, apply all SC2086)
IFS=,; set -f; set --"$input"; out=
 
Line 125:
echo "" >> /tmp/crew.txt
^-- SC2129: Consider using { cmd1; cmd2; } >> file instead of individual redirects.
 
Line 130:
let a++
^-- SC2219: Instead of 'let expr', prefer (( expr )) .

$ 

我建议您仔细查看第 62 行和第 64 行的输出消息。

请注意,如果您通过 ShellCheck 自行运行脚本,您将看到输出包含可点击的链接,以获取有关生成的各种消息的更多信息。

相关内容