完善Bash循环

时间:2018-09-24 16:31:18

标签: arrays bash loops sh

Hello Stackoverflow社区,

请原谅我的想法,但是我有一个基本的循环脚本,它运行着一个更复杂的脚本,该脚本正在寻找文本文件以作为数组输入。我想它可以正常工作,但是我知道它可以更好,更自动化地运行。

这是脚本要查找的文本文件;

2014;14204;
2015;15042;
2015;15062;
...
end;

这是我作为循环运行的bash脚本;

{ while IFS=';' read  YEAR1 PROJ1 YEAR2 PROJ2 YEAR3 PROJ3 fake
    do 
        { echo "$YEAR1" | egrep '^#|^ *$' ; } > /dev/null && continue 
            $local/script.sh \
                --forinput1 $YEAR1/$PROJ1  \
                --forinput2 $YEAR2/$PROJ2  \
                --forinput3 $YEAR3/$PROJ3  \
    done 
} < textFile.txt

我自己进行了一些研究,发现了一些我认为可以解决的问题,但未能正确实施。如果您能给我一些指导,我将不胜感激。

编辑: 我确实表示歉意,因此脚本确实将文本文件识别为:

YEAR1;PROJ1;
YEAR2;PROJ2;
YEAR3;PROJ3;

使用“;”作为其分隔符。它确实会循环运行,直到完成最后一个变量。但是,要实现此功能,我需要在文本文件中添加额外的行

YEAR4;PROJ4;
YEAR5;PROJ5;
end;

然后添加脚本

{ while IFS=';' read  YEAR1 PROJ1 YEAR2 PROJ2 YEAR3 PROJ3 YEAR4 PROJ4 YEAR5 PROJ5 fake
    do 
        { echo "$YEAR1" | egrep '^#|^ *$' ; } > /dev/null && continue 
            $local/script.sh \
                --forinput1 $YEAR1/$PROJ1  \
                --forinput2 $YEAR2/$PROJ2  \
                --forinput3 $YEAR3/$PROJ3  \
                --forinput4 $YEAR4/$PROJ4  \
                --forinput5 $YEAR5/$PROJ5  \
    done 
} < textFile.txt

我希望完成的是在数组中添加变量,而不必在脚本中添加额外的语法

这很坏,但我想我正在寻找的是

{ while IFS=';' read -a YEAR PROJ < textFile.txt
for ((i = 0; i < "${#YEAR[@]};${#PROJ[@]}"; i++)); do
{ echo "$YEAR[$i]" | egrep '^#|^ *$' ; } > /dev/null && continue 
                $local/script.sh \
                    --forinput[$i] ${YEAR[$i]}/${PROJ[$i]}  \
        done 
    }

2 个答案:

答案 0 :(得分:0)

我假设您的输入文件包含3行的组,每行2个字段,而不是6个字段的行。

$ cat file
y1a;p1a;
y2a;p2a;
y3a;p3a;
y1b;p1b;
y2b;p2b;
y3b;p3b;

然后,您可以将多个读取命令作为while“ condition”:

while
     IFS=';' read year1 proj1 x
     IFS=';' read year2 proj2 x
     IFS=';' read year3 proj3 x
do
    echo script \
        --forinput1 "$year1/$proj1" \
        --forinput2 "$year2/$proj2" \
        --forinput3 "$year3/$proj3"
done < file
script --forinput1 y1a/p1a --forinput2 y2a/p2a --forinput3 y3a/p3a
script --forinput1 y1b/p1b --forinput2 y2b/p2b --forinput3 y3b/p3b

但是,这不能处理注释和空白行。此版本在第三行非(注释/空白)行之后执行脚本

$ cat file
# set 1
y1a;p1a;
y2a;p2a;
y3a;p3a;

# set 2
y1b;p1b;
y2b;p2b;
y3b;p3b;

n=0
args=()
while IFS=';' read year project x; do
    { [[ $year == *([[:blank:]])"#"* ]] || [[ $year == *([[:blank:]]) ]]; } && continue
    ((n++))
    args+=( "--forinput$n" "$year/$project" )
    if (( n == 3 )); then 
        echo script "${args[@]}"
        args=()
        n=0
    fi
done < file
script --forinput1 y1a/p1a --forinput2 y2a/p2a --forinput3 y3a/p3a
script --forinput1 y1b/p1b --forinput2 y2b/p2b --forinput3 y3b/p3b

另一种处理每组任意记录的方法:

$ cat file
# set 1
y1a;p1a;
y2a;p2a;
y3a;p3a;
y4a;p4a;
y5a;p5a;
end;

# set 2
y1b;p1b;
y2b;p2b;
y3b;p3b;
end;

$ grep -Pv '^\s*(#|$)' file | awk -F"\n" -v RS="\nend;\n" -v OFS=, '{
    cmd = "script.sh"
    for (i=1; i<=NF; i++) {
        n = split($i, a, /;/)
        cmd = sprintf( "%s --forinput%d \"%s/%s\"", cmd, i, a[1], a[2])
    }
    print cmd
}'
script.sh --forinput1 "y1a/p1a" --forinput2 "y2a/p2a" --forinput3 "y3a/p3a" --forinput4 "y4a/p4a" --forinput5 "y5a/p5a"
script.sh --forinput1 "y1b/p1b" --forinput2 "y2b/p2b" --forinput3 "y3b/p3b"

使用grep过滤掉注释和空白行。然后awk来格式化命令:

  • RS记录分隔符,使用end;
  • -F"\n"字段分隔符设置为换行符
  • 然后,我们遍历字段数(NF)来构造要运行的命令,并将其打印出来。

要实际执行它,请将awk输出通过管道传递到| sh

答案 1 :(得分:0)

您的代码建议每个输入行都有6个字段,这可能导致类似的代码

sed -nr 's#([^;]*);([^;]*);([^;]*);([^;]*);([^;]*);([^;]*);#$local/script.sh --forinput \1/\2 --forinput \3/\4 --forinput \5/\6#p' textFile.txt
# or shorter
f='([^;]*;)'
sed -nr 's#'$f$f$f$f$f$f'#$local/script.sh --forinput \1/\2 --forinput \3/\4 --forinput \5/\6#p' textFile.txt

当您必须将三行输入结合在一起时,您不应该变得聪明。

# Don't do this
cat textFile.txt | paste -d'X' - - - | tr -d 'X'
# Trying to make
sed -nr 's#'$f$f$f$f$f$f'#$local/script.sh --forinput \1/\2 --forinput \3/\4 --forinput \5/\6#p' <(cat textFile.txt | paste -d'X' - - - | tr -d 'X')

自豪地展示代码后,您会发现当第二行是注释(以'#'开头)时,您将不得不使代码变得更糟。您需要将cat textFile.txt替换为grep -Ev '^#|^ *$' testFile.txt

当您需要关联不同的行时,请查看awk
没有检查的解决方案是

awk -F';' '{line++}
{param=param " --forinput " $1 "/" $2}
line==3 {print "$local/script.sh" param ; param=""; line=0}
' textFile.txt

您可以添加各种检查。