【问题标题】:Process files in pairs成对处理文件
【发布时间】:2017-01-12 16:18:08
【问题描述】:

我有一个文件列表:

file_name_FOO31101.txt
file_name_FOO31102.txt
file_name_FOO31103.txt
file_name_FOO31104.txt

我想使用成对的文件输入到下游程序中,例如:

program_call file_name_01.txt file_name_02.txt
program_call file_name_03.txt file_name_04.txt
...

我不想要:

program_call file_name_02.txt file_name_03.txt

我需要循环执行如下:

#!/bin/bash

FILES=path/to/files

for file in $FILES/*.txt;

do

    stem=$( basename "${file}" ) # stem : file_name_FOO31104_info.txt
    output_base=$( echo $stem | cut -d'_' -f 1,2,3 )  # output_base : FOO31104_info.txt
    id=$( echo $stem | cut -d'_' -f 3 ) # get the first field : FOO31104
    number=$( echo -n $id | tail -c 2 ) # get the last two digits : 04

     echo $id $((id+1))

done

但这不会产生我想要的。

在每个循环中,我想调用一个程序一次,输入两个文件(第一个文件的最后 2 位总是奇数 01,第二个文件的后 2 位总是偶数 02

【问题讨论】:

  • 顺便说一句,全大写的变量名是specified by POSIX,供对系统或shell有意义的变量使用,而小写的变量名则保留给应用程序使用。考虑在您自己的代码中遵循这一点,以防止任何可能错误地踩踏对系统有意义的变量。 (这个规范对环境变量是明确的,但是设置一个常规的 shell 变量会覆盖任何类似命名的环境变量,所以它们共享一个命名空间)。
  • 顺便说一句,如果您的 FILES 路径包含任何空格,$FILES/*.txt 将中断,因此 "$FILES"/*.txt
  • @CharlesDuffy - 谢谢 - 很高兴知道这一点
  • 顺便说一句,与本机字符串操作原语相比,使用 cuttail 等外部命令的所有操作都非常慢。我在答案中添加了附录;另请参阅 BashFAQ #100the bash-hackers page on parameter expansion
  • ...对我的回答所涉及的内容提供更完整的解释,回复:$((id + 1))——假设你的意思是$(( number + 1 ))(因为$id是一个以开头的字符串非数字字符):如果数字的第一个字符是0,则将其解释为八进制;因此,echo $(( 010 )) 发出 8,echo $(( 09 )) 导致“值对于基础而言太大”错误;因此在我的回答中需要$((10#$number)) 以强制解释为十进制。

标签: bash for-loop file-processing


【解决方案1】:

我实际上根本不会使用for 循环。 shifts 文件关闭的 while 循环是执行此操作的完全合理的方法。

# here, we're overriding the argument list with the list of files
# ...you can do this in a function if you want to keep the global argument list intact
set -- "$FILES"/*.txt                 ## without these quotes paths with spaces break

# handle the case where no files were found matching our glob
[[ -e $1 || -L $1 ]] || { echo "No .txt found in $FILES" >&2; exit 1; }

# here, we're doing our own loop over those arguments
while (( "$#" > 1 )); do              ## continue in the loop only w/ 2-or-more remaining
  echo "Processing files $1 and $2"   ## ...substitute your own logic here...
  shift 2 || break                    ## break even if test doesn't handle this case
done

# ...and add your own handling for the case where there's an odd number of files.
(( "$#" )) && echo "Left over file $1 still exists"

请注意,$#s 在这里被引用在 (( )) 中,用于 StackOverflow 的语法高亮显示,而不是因为它们需要被引用。 :)


顺便说一句——考虑使用 bash 的原生字符串操作。

stem=${file##*/}
IFS=_ read -r p1 p2 id p_rest <<<"$stem"
number=${id:$(( ${#id} - 2 ))}
output_base="${p1}${p2}${id}"
echo "$id $((10#number + 1))" # 10# ensures interpretation as decimal, not octal

【讨论】:

  • 请注意,这种方法甚至适用于非常大的目录。
  • 是的——这有点不明显,因为在启动脚本时不能将参数向量(和环境变量集)超过某个(操作系统相关)大小传递到脚本中,但是你可以稍后仍使用set 将其替换为更大的列表。
  • 请注意,shift 2 等效于 shift; shift - 至少在 bash 中。
  • @choroba,不完全等价——如果没有两个参数需要移动,shift 2 将什么都不做,这意味着需要注意不要在while (( $# )) 循环中使用它,在这种情况下,如果shift 2 上没有|| break,这可能是无穷无尽的。在这种情况下 - 条件与 (( $# == 1 )) 中断 - 不过,是的,即使没有明确的 break,两者都完全可用。
  • @fugu,当用作set -- arg1 arg2 ...时,将$1设置为arg1$2设置为arg2等;这些变化同样反映在$#(指参数数量)和"$@"(指整个集合)等语法中。
猜你喜欢
  • 2023-01-05
  • 1970-01-01
  • 1970-01-01
  • 2016-06-06
  • 1970-01-01
  • 2018-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多