有没有一种方法可以指示输入文件行循环中的最后一次迭代?

有没有一种方法可以指示输入文件行循环中的最后一次迭代?

假设我有一个direction包含以下行的文件

east
north
south
west
south-west

并使用循环并echo在 shell 脚本中生成以下输出:

Direction: east
Direction: north
Direction: south
Direction: west
Last direction: south-west

换句话说,我想对脚本中的最后一行做一些不同的事情。

答案1

bash无法检测文件的结尾(不尝试读取下一行并失败),但 perl 可以使用其eof功能:

$ perl -n -e 'print "Last " if eof; print "Direction: $_"' direction 
Direction: east
Direction: north
Direction: south
Direction: west
Last Direction: south-west

注意:与echobash 不同,printperl 中的语句不会打印换行符,除非您 1. 通过包含\n在要打印的字符串中明确告诉它,或者 2. 使用 perl 的-l命令行选项,或者 3. if字符串已经包含换行符......就像$_- 的情况一样,这就是为什么你经常需要chomp()它来删除换行符。

BTW,在 perl 中,$_是当前输入行。或者任何未指定实际变量名称的循环中的默认迭代器(通常称为“当前事物”,可能是因为“dollarunderscore”有点拗口)。许多$_如果未提供,perl 函数和运算符将用作默认/隐式参数。查看man perlvar并搜索$_.

sed也可以 -$地址与文件的最后一行匹配:

$ sed -e 's/^/Direction: /; $s/^/Last /' direction 
Direction: east
Direction: north
Direction: south
Direction: west
Last Direction: south-west

规则的顺序sed很重要。我的第一次尝试以错误的方式进行了(并打印了“方向:最后的西南方向”)。此 sed 脚本始终将“Direction:”添加到每行的开头。在最后一行 ( $) 中,它将“Last ”添加到已被前一条语句修改的行的开头。

答案2

循环无法知道何时到达终点,除非它确实到达终点。因此,要么处理文件两次,一次获取行数,另一次输出,或者打印每次迭代时的前一个值和退出循环时的最后一个值:

prev=""
while read direction; do 
    if [ -n "$prev" ]; then 
        echo "Direction: $prev"
    fi
    prev="$direction"
done < direction
echo "Last Direction: $prev" 

答案3

的一个变体FelixJN 的回答,将行读入数组,但使用 bash 内置函数而不是 bash 循环:

mapfile -t DIR < direction
printf "Direction: %s\n" "${DIR[@]::${#DIR[@]}-1}"
printf "Last direction: %s\n" "${DIR[-1]}"

地图文件是 Bash 4 中添加的内置函数,它将标准输入读取到数组中,每个条目一行。该-t选项会去除换行符。

printf将重复应用其格式字符串,直到用完所有输入参数。

答案4

您可以混合使用paste,tailhead

$ paste -s -d'\n' <(head -n -1 direction | sed 's/^/Direction: /') <(tail -n1 direction | sed 's/^/Last direction: /')
Direction: east
Direction: north
Direction: south
Direction: west
Last direction: south-west

  • paste -s -d'\n'使用换行符作为分隔符-d'\n',并带有选项 和 选项-s

    -s, --serial
    一次粘贴一个文件而不是并行粘贴

  • head -n -1检索除最后一行之外的所有行,然后通过管道传输到sed仅在这些行中执行您需要的命令。

  • tail -n1 file检索文件的最后一行,然后通过管道传输到sed仅执行该行中所需的命令。


或者更简洁地只是使用cat而不是paste,如所指出的@米克·马泰奥的评论

$ cat <(head -n -1 direction | sed 's/^/Direction: /') <(tail -n1 direction | sed 's/^/Last direction: /')

相关内容