如何使用不精确文件名的输入文件进行操作?

如何使用不精确文件名的输入文件进行操作?

我有某个目录中文件的文件名文件。但是,某些文件名可能:

  1. 将原始文件名中的空格替换为下划线(因此在输入文件中directory/file with spaces变为如此)file_with_spaces
  2. 实际上可能与目录中的文件不匹配

如果我没有这两个条件,我会用来cat inputfile | awk 'commands'处理在文件上应用我想要的命令。但是,我想要某种方法来捕获文件名未找到错误,并且:

  1. 尝试用空格替换下划线的不同组合,直到找到匹配的文件
  2. 提供没有匹配项的文件列表,即使在用空格替换下划线之后也是如此。

有没有好的方法可以做到这一点?我怀疑需要某种类型的脚本(而不是单行命令),但我对 shell 脚本还不够熟悉,无法想到解决方案。

答案1

我使用的方法是获取 ls 的输出,设置一个将转换后的名称映射回原始名称的数组,然后处理输入文件的每一行。如果输入在数组中,则输出数组的值,否则将输入行添加到文件中missing。所以就像把它放在一个文件中,更改为并作为参数directory运行它inputfile

#!/usr/bin/awk
# set up an array t of translations
BEGIN {
    while (("ls" | getline )>0) {
        k=$0
        gsub(/ /,"_")
        if ($0 in t) {
            print "$0 matches more than one file" > /dev/stderr
            exit(2)
        }
        t[$0]=k
    }
    close("ls")
}
    { if ($0 in t) {
            print t[$0]
        } else {
            print $0 > "../missing"
        }
    }

答案2

将修改后的文件名转换为与原始文件匹配的模式。

#!/bin/bash
shopt -s nullglob extglob
IFS=$'\n'
while read -r filename; do
  pattern=${filename//\\/\\\\}
  pattern=${pattern//\[/\\\[}
  pattern=${pattern//\(/\\\(}
  pattern=${pattern//\*/\\\*}
  pattern=${pattern//\?/\\\?}
  pattern=${pattern//_/'[ _]'}
  matches=($pattern@())
  case ${#matches[@]} in
    0) echo "No match for $filename";;
    1) echo "$filename found as ${matches[0]}";;
    *) echo "$filename matches ${#matches[@]} files";;
  esac
done <inputfile

答案3

通过zsh,您可以使用它的近似匹配功能:

approx-cat() {
  emulate -L zsh
  setopt extendedglob nullglob
  local err files
  for ((err = 0; err <= $1; err++)); do
    files=((#a$err)$2)
    case $#files in
      (1) cat -- $files; return;;
      (0) ;;
      (*) echo >&2 "$#files found at error count $err:"
          printf >&2 '  "%s"\n' $files
          return 1;;
    esac
  done
  return 1
}

并调用为:

approx-cat 3 'directory/file with spaces'

最多允许 3错误在文件名中。

例子:

$ approx-cat 3 /ebc/passwds
2 found at error count 2:
  "/etc/passwd"
  "/etc/passwd-"
$ approx-cat 3 /ebc/Issue
Debian GNU/Linux stretch/sid \n \l

相关内容