【问题标题】:Bash: filtering and replacing by patternBash:按模式过滤和替换
【发布时间】:2020-12-27 09:21:36
【问题描述】:

给定这串路径,用空格分隔:

path/folderA/fileA1 path/folderA/subFolderA/fileA2 path/folderB/fileB1
  1. 我想得到一串路径,用空格分隔,只有以 path/folderA/ 开头的路径。
    输出:path/folderA/fileA1 path/folderA/subFolderA/fileA2

  2. 然后从该字符串中删除 path/folderA/ 的任何匹配项。
    最终输出:fileA1 subFolderA/fileA2

这可以用一行来完成吗?

【问题讨论】:

  • 您可以使用命令分隔符将任何内容转换为单行,但您不能以我所知道的任何语言将其作为单个命令执行。 awk、perl 或 ruby​​ 会更容易简洁地解析字符串; bash 可以做很多事情,但简约并不是它的强项。

标签: arrays string bash filter replace


【解决方案1】:

如果您以字符串开头,则存在嵌入空格、换行符或其他有问题的字符可能会造成问题的风险。这就是为什么使用 glob 或以 null 结尾的值通常会更好的原因。

也就是说,您可以使用各种内置函数和扩展来从给定示例中获得所需的结果。请注意,您必须正确转义正斜杠或将它们存储在带引号的字符串中以避免干扰扩展语法。例如:

path_str="path/folderA/fileA1 path/folderA/subFolderA/fileA2 path/folderB/fileB1"
match_str="path/folderA/"

read -ra paths <<< "$path_str"
for i in "${!paths[@]}"; do
    [[ ! "${paths[i]}" =~ $match_str ]] && unset paths[i]
done

echo "${paths[@]//$match_str}"

这将打印:

fileA1 subFolderA/fileA2

【讨论】:

  • 不应该match_str^开头来满足“path/folderA/开头”的要求吗?
  • @BenjaminW。是的,对。我在回答中也错过了这一点并修复了。谢谢。
【解决方案2】:

grep

echo " $str" | grep -oP '(?<=\spath/folderA/)\S+' | xargs

-P 启用 Perl 正则表达式语法,您可以使用(?&lt;=pattern),这是一个积极的后向断言。同样-o 只保留该模式之后的匹配部分,即\S+,一系列非空白字符(直到我们找到下一个空格、制表符、换行符等)

此外,grep 输出总是由换行符分隔,因此您必须通过管道传输到 tr '\n' ' 'xargs 或类似的管道才能获得一行。

编辑: 为了只匹配路径的开头,我添加了\s(一个空格字符)并将输入作为" $str" 提供。这似乎更容易解决,因为\b 也匹配/,并且(^|\s) 抛出grep: lookbehind assertion is not fixed length。所以用这个测试是可以的:

> echo "$str"
path/folderA/fileA1 path/folderA/subfolderA/fileA2 path/path/folderA/not
> echo " $str" | grep -owP '(?<=\spath/folderA/)\S+' | xargs
fileA1 subFolderA/fileA2

【讨论】:

    【解决方案3】:

    您可以简单地使用awk 匹配每个字段中的最后一组单词字符并输出它们,例如

    awk '{for (i=1; i<=NF; i++) if ($i ~ /folderA/) { match($i,/\w+$/); print substr($i,RSTART,RLENGTH)}}' <<< $path_str
    

    使用/输出示例

    path_str="path/folderA/fileA1 path/folderA/subFolderA/fileA2 path/folderB/fileB1"
    awk '{for (i=1; i<=NF; i++) if ($i ~ /folderA/) { match($i,/\w+$/); print substr($i,RSTART,RLENGTH)}}' <<< $path_str
    fileA1
    fileA2
    

    您可以根据需要调整输出格式。如果您希望输出全部在一行上,或者如果您想使用 命令替换 将输出捕获到一个新数组中,这取决于您。

    使用 Bash 参数扩展

    如果您想使用 参数扩展子字符串删除,您可以使用一个简单的循环和扩展 $(var##*/} 来删除直到最终 '/' 的所有内容从每个路径组件,例如

    path_str="path/folderA/fileA1 path/folderA/subFolderA/fileA2 path/folderB/fileB1"
    for i in $path_str; do 
        [[ $i =~ folderA ]] && echo ${i##*/}
    done
    fileA1
    fileA2
    

    对于您的情况,参数扩展可能是最有效的,因为它是您的 shell 的内置功能,并且可以避免产生子 shell。但是,如果您有数十万个组件,我可能会让awk 处理它。

    符合 POSIX 且去除子字符串的参数扩展集是:

    ${var#pattern}      Strip shortest match of pattern from front of $var
    ${var##pattern}     Strip longest match of pattern from front of $var
    ${var%pattern}      Strip shortest match of pattern from back of $var
    ${var%%pattern}     Strip longest match of pattern from back of $var
    

    除了 POSIX 提供的参数扩展之外,Bash 还提供了很多很多的参数扩展。包括从子字符串替换到字符大小写转换的所有内容。

    如果您还有其他问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 2022-01-06
      • 2021-11-17
      • 2011-08-16
      • 2018-06-02
      • 2018-02-15
      • 2017-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多