如何精确匹配 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 .............. >
正如在回答您的上一个问题(已删除)时提到的,解析
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
。
由于目标可能与要返回的部分相同,也可能不同,似乎最容易进行两个单独的正则表达式调用(如果应该通用,也可能有帮助):
perl -ne 'print "$1" if (m#[email protected]# && m#([^/]+)\s*$#);'
换句话说,找到目标短语,然后获取 find 的最后一个组成部分(不包含“/”的部分)。当两个条件都满足时,打印括号中捕获的文本。
关于特殊字符: 如果“#”被替换为更传统的“/”,你需要转义我写的“/”,否则“@”不会给你带来麻烦。当然,如果你的系统上有这种情况,只需用“\”转义即可。
鉴于以下链接
$ 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
尝试这样做:
#!/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