【问题标题】:find directories but exclude list where directories have a space in name查找目录但排除目录名称中有空格的列表
【发布时间】:2018-12-20 19:16:02
【问题描述】:

我有一个进程在大型文件系统上从一天到另一天审核文件。我想通过使用要排除的目录列表来排除一些目录。我可以这样做,但如果排除目录的名称中有空格,我会遇到麻烦。

为简单起见,我只列出四个子目录,但实际上我要搜索与排除的目录要多得多。也有可能添加一个新目录并且我想自动包含新目录,因此排除列表与使用包含列表。

base_dir/
├── sub_dir1
├── sub_dir2
├── sub dir3
└── sub_dir4

我有一个 shell 脚本和一个排除列表

$ cat exclude.txt
sub_dir2
sub dir3

shell 脚本使用findprintf 以及awk 和sort 来获取要审核的目录列表。

$ find ./base_dir -maxdepth 1 -type d $(printf "! -iname %s " $(cat exclude.txt)) | awk -F/ '{print $NF}' | sort
sub_dir1
sub dir3
sub_dir4

正如您可能猜到并在上面看到的那样,除了它没有忽略sub dir3 之外,它是有效的。我在排除列表中尝试了一些双引号组合,并使用%q vs %s vs %a,但似乎找不到正确的组合。

我想要的输出是

sub_dir1
sub_dir4

我意识到我可以这样做:

find ./base_dir -maxdepth 1 -type d \
    ! -iname "sub dir3" $(printf "! -iname %s " $(cat exclude.txt)) \
    | awk -F/ '{print $NF}' | sort

并获得我预期的输出,但我只想使用exclude.txt 列表。

编辑 在阅读了一些回复后,我尝试使用数组并认为它会起作用,现在我更加难以理解为什么这个选项不起作用。 printf 似乎会生成一个字符串,如果我将它严格输入到命令行中,它会起作用,但是当尝试将它作为单行符运行时仍然会给我错误。

$cat exclude.txt
base_dir
sub_dir2
"sub dir3"

$ mapfile -t exclude < exclude.txt

$printf "! -iname %s " "${exclude[@]}"
! -iname base_dir ! -iname sub_dir2 ! -iname "sub dir3"

$find ./base_dir -maxdepth 1 -type d $(printf "! -iname %s " "${exclude[@]}")
find: paths must precede expression: dir3"

$ find ./base_dir -maxdepth 1 -type d ! -iname base_dir ! -iname sub_dir2 ! -iname "sub dir3"
./base_dir/sub_dir1
./base_dir/sub_dir4

【问题讨论】:

  • 这是一个猜测:在您的排除列表中,您是否尝试过使用反斜杠而不是使用引号? sub\ dir3.
  • 是的,对不起,没有提到,我也试过文件中的反斜杠
  • 对于编辑,请参阅stackoverflow.com/questions/51307638/… 询问几乎所有内容。
  • 您评论中的链接是指向该主题的链接....您是要发布另一个链接吗?

标签: bash find printf


【解决方案1】:

已编辑以包含新信息,以防以后有用

不要嵌入 printf/cat。解释器解析器对您不利。 将带有paste -s 的排除过滤器堆叠到一个临时文件中以动态构建您的命令,然后执行它。

$: find ./base_dir
./base_dir
./base_dir/sub dir1
./base_dir/sub dir3
./base_dir/sub_dir1
./base_dir/sub_dir3

$: tmpfile=/tmp/xFinder
$: printf "find ./base_dir -maxdepth 1 -type d ! -iname base_dir " > $tmpfile
$: { sed -E 's/^(.*)/! -iname \"\1\"/' exclude.txt; 
     printf " | xargs -I R basename R "; } | paste -s >> $tmpfile
$: cat $tmpfile
find ./base_dir -maxdepth 1 -type d ! -iname base_dir ! -iname "sub_dir1"    ! -iname "sub dir3"     ! -iname "sub_dir4"      | xargs -I R basename R

对 basname 的 xargs 调用会去除路径信息,! -iname base_dir 会将其作为自己的目录保留在查找输出之外。

$: . $tmpfile
./base_dir
./base_dir/sub dir1
./base_dir/sub_dir3

对早期的不完整版本表示歉意。

【讨论】:

  • 你运行成功了吗?由于它输出多行,我尝试并得到一个错误
  • 我是测试不足的受害者和意外拖钓的肇事者,应该受到严厉批评和反对,直到我删除帖子,或者至少修复它。我无意中嵌入了',它们破坏了代码的实际功能。 Mea Culpa,真诚的道歉,我会看看我能做些什么来纠正它。给我几分钟。
  • 已更新。再次道歉。
  • 大声笑,没问题,只是不确定我自己是否抄错了。感谢您的跟进
  • 感谢您的时间和信息,我最终使用了带有 iregex 和 regextype 的其他解决方案,因为它只给了我没有路径的子目录名称。我试图开始将 awk 语句添加到您的建议中,但时间不够用了,就走了另一条路。我确实认为这也可以。
【解决方案2】:

您可以将排除文件读入 Bash 数组,然后像这样编写 find 命令:

mapfile -t exclude < exclude.txt
find ./base_dir \
    -mindepth 1 \          # Exclude the current directory
    -type d \
    -regextype egrep \     # Make sure alternation "|" does not have to be escaped
    ! -iregex ".*/($(IFS='|'; echo "${exclude[*]}"))" \
    -printf '%f\n'         # Print just filename without leading directories

导致

sub_dir1
sub_dir4

对于您的示例输入,-iregex 测试扩展如下:

$ IFS='|'
$ echo "${exclude[*]}")
sub_dir2|sub dir3

所以排除路径的正则表达式变为

.*/(sub_dir2|sub dir3)

IFS 的更改仅限于命令替换。

对此的限制是,如果要排除的目录包含正则表达式特有的字符,则必须转义这些字符,这可能会变得混乱。如果你想逃脱,例如管道,你可以使用

echo "${exclude[*]//|/\\|}"

在命令替换中,导致

sub_dir2|sub dir3|has\|pipe

名称中带有| 的目录has|pipe 的管道已正确转义。

【讨论】:

    【解决方案3】:

    由于您只想限制单个子目录,无需递归,您可以使用带有通配符的 for 循环:

    $ find base_dir/
    base_dir/
    base_dir/sub_dir2
    base_dir/sub_dir1
    base_dir/sub_dir4
    base_dir/sub dir3
    
    $ cat exclude.txt 
    sub_dir2
    sub dir3
    
    $ cat script.sh 
    #!/bin/bash
    for dir in base_dir/*
    do
      ! [ -d "$dir" ] || 
        grep -qFx -- "$(basename -- "$dir")" exclude.txt &&
        continue
      echo "$dir" # or do somthing else
    done
    
    $ ./script.sh 
    base_dir/sub_dir1
    base_dir/sub_dir4
    

    【讨论】:

      猜你喜欢
      • 2019-04-10
      • 1970-01-01
      • 2013-02-08
      • 1970-01-01
      • 2019-03-19
      • 2011-02-20
      • 1970-01-01
      • 2013-07-08
      • 2015-09-26
      相关资源
      最近更新 更多