Perl oneliner 可在具有特殊字符的许多不同值上匹配路径中的精确单词

问题描述 投票:0回答:4

如何精确匹配 find /tmp -type l -exec ls -l 输出中的 $TARGET_NAME 值?

 $ find /tmp -type l -exec ls -l 2>/dev/null {} +
 lrwxrwxrwx 1 root root  24 Mar 18 12:41 /tmp/test/link -> /usr/admin/Collect_tests
 lrwxrwxrwx 1 root root  43 Mar 18 12:41 /tmp/test/link1 -> /usr/admin/Collect_tests/[email protected]
 lrwxrwxrwx 1 root root  68 Mar 18 12:41 /tmp/test/link2 -> /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com
 lrwxrwxrwx 1 root root 100 Mar 18 12:42 /tmp/test/link3 -> /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com/List.files.emails.dummy*Printed
 lrwxrwxrwx 1 root root  92 Mar 18 12:42 /tmp/test/link4 -> /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com/[email protected]

价值观示例

 [email protected]
 TARGET_NAME=Upload_Shema@@@.DATA.com
 TARGET_NAME=List.files.emails.dummy*Printed

目标:仅当 $TARGET_NAME 时才打印:“链接名称”和“路径”(最后一个字段) 与最后一个字段中的单词完全匹配。

示例(当我们想要精确匹配时 - 而 TARGET_NAME=Upload_Shema@@@.DATA.com 然后):

结果将显示如下

/tmp/test/link2 /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com
/tmp/test/link3 /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com/List.files.emails.dummy*Printed
/tmp/test/link4 /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com/[email protected]

有几个条件:

1)只需要匹配最后一个字段(来自

ls -l
输出)

示例

      /usr/admin/Collect_tests/[email protected]

2) $TARGET_NAME 值应匹配整个单词

完整匹配示例(同时 [电子邮件受保护]):

    /usr/admin/Collect_tests/[email protected]

非完整匹配示例:

    /usr/admin/Collect_tests/[email protected]

3) $TARGET_NAME 左侧必须存在反斜杠(“/”),$TARGET_NAME 右侧必须存在反斜杠或字符串结尾。

4)需要转义特殊字符:“ / ”,“ @ ”。 “*”等

5) 代码将成为 ksh 脚本的一部分(并且可以由 Perl oneliner 或 AWK 或 ksh 等实现。)

示例

   find /tmp -type l -exec ls -l 2>/dev/null {} + | < Perl one liner .............. >    
regex linux bash perl ksh
4个回答
0
投票

正如在回答您的上一个问题(已删除)时提到的,解析

ls
输出非常不理想。可以用
readlink
代替。

find /tmp -type l -exec \
   perl -e'
      my $TARGET_NAME = shift;
      for (@ARGV) {
         my $p = readlink($_);
         $p =~ m{(?:^|/)\Q$TARGET_NAME\E(?:/|\z)}
            or next;
         print("$_\t$p\n");
      }
   ' "$TARGET_NAME" {} \;

或者更有效,

perl -MFile::Find::Rule -e'
   my ($TARGET_NAME, $BASE) = @ARGV;
   for (File::Find::Rule->symlink->in($BASE)) {
      my $p = readlink($_);
      $p =~ m{(?:^|/)\Q$TARGET_NAME\E(?:/|\z)}
         or next;
      print("$_\t$p\n");
   }
' "$TARGET_NAME" /tmp

按照要求,这将匹配

TARGET_NAME
TARGET_NAME/
TARGET_NAME/x
.../TARGET_NAME
.../TARGET_NAME/
.../TARGET_NAME/x

但不是

TARGET_NAMEx/...
.../TARGET_NAMEx
.../TARGET_NAMEx/...
xTARGET_NAME/...
.../xTARGET_NAME
.../xTARGET_NAME/...

注意:如果您的

find ... -exec ... \;
支持,请将
find ... -exec ... +
更改为
find


0
投票

由于目标可能与要返回的部分相同,也可能不同,似乎最容易进行两个单独的正则表达式调用(如果应该通用,也可能有帮助):

perl -ne 'print "$1" if (m#[email protected]# && m#([^/]+)\s*$#);'

换句话说,找到目标短语,然后获取 find 的最后一个组成部分(不包含“/”的部分)。当两个条件都满足时,打印括号中捕获的文本。

关于特殊字符: 如果“#”被替换为更传统的“/”,你需要转义我写的“/”,否则“@”不会给你带来麻烦。当然,如果你的系统上有这种情况,只需用“\”转义即可。


0
投票

鉴于以下链接

$ cd /tmp

$ ls -l link* | sed -e 's/^.*\(link\)/\1/'
link -> /usr/admin/Collect_tests
link1 -> /usr/admin/Collect_tests/[email protected]
link2 -> /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com
link3 -> /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com/List.files.emails.dummy*Printed
link4 -> /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com/List.files.emails.dummy
link5 -> /usr/admin/Collect_tests/[email protected]/

使用 File::Find 模块,如

$ TARGET_NAME='Upload_Shema@@@.DATA.com' perl -MFile::Find -le 'find sub {
   -l && defined($dst = readlink $_) &&
   index($dst, $ENV{TARGET_NAME}) >= 0 &&
   print "$File::Find::name $dst" }, @ARGV' /tmp
/tmp/link2 /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com
/tmp/link3 /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com/List.files.emails.dummy*Printed
/tmp/link4 /usr/admin/Collect_tests/[email protected]/Upload_Shema@@@.DATA.com/List.files.emails.dummy

作为一句台词来说,这确实很笨拙。作为一个单独的命令,它变成了

#! /usr/bin/env perl

use strict;
use warnings;

use File::Find;

die "Usage: $0 root-dir ..\n" unless @ARGV;
die "$0: TARGET_NAME is not defined\n" unless exists $ENV{TARGET_NAME};

sub print_matching_target_name {
  return unless -l && defined(my $dst = readlink $_);
  print "$File::Find::name $dst\n" if index($dst, $ENV{TARGET_NAME}) >= 0;
}

find \&print_matching_target_name, @ARGV;

输出示例:

$ 查找目标
用法:查找目标根目录..

$ 查找目标 /tmp
查找目标:TARGET_NAME 未定义

$ [电子邮件受保护] ./find-target /tmp
/tmp/link2 /usr/admin/Collect_tests/[电子邮件受保护]/Upload_Shema@@@.DATA.com
/tmp/link3 /usr/admin/Collect_tests/[电子邮件受保护]/Upload_Shema@@@.DATA.com/List.files.emails.dummy*打印
/tmp/link4 /usr/admin/Collect_tests/[电子邮件受保护]/Upload_Shema@@@.DATA.com/List.files.emails.dummy

0
投票

尝试这样做:

#!/bin/bash

while IFS= read -r file; do
    printf "TARGET_NAME=%q\n" "$file"
done < <(find /tmp -type l -printf '%l\n')

结果带反斜杠,例如:

TARGET_NAME=/tmp/foo/List.files.emails.dummy\*Printed
© www.soinside.com 2019 - 2024. All rights reserved.