awk 脚本中的管道

awk 脚本中的管道

我正在尝试编写一个ls包装器,用于awk解析ls -lhF.现在我已将程序分成两个文件 -my_ls.shmy_ls.awk.my_ls.sh的唯一目的是ls -lhF将的输出通过管道传输到my_ls.awk。看起来像:

#!/bin/bash
ls -lhF "$@" | my_ls.awk

ls -lhF我想知道是否有任何方法可以通过 awk 脚本本身读取输出。

编辑:我的主要目的是编写一个脚本,以漂亮的树的形式显示当前目录内容。的草稿版本my_ls.awk如下所示:

#!/usr/bin/awk -f

( NF >= 9 ) {
    print "|-- [" $5 "] " $9
}

这是我到目前为止所达到的目标。

答案1

我将加入其他建议,即您不应解析 的输出ls,因此这是一个不好的例子。但作为更一般的问题,我将通过将 awk 脚本作为参数传递给awk.

#!/bin/bash
ls -lhF "$@" | awk '
    ( NF >= 9 ) {
        print "|-- [" $5 "] " $9
    }'

请注意,如果 awk 脚本必须包含'(单引号) 字符,则需要将其加引号:use '\''(闭单引号、文字单引号、开单引号)。

为了避免引用,您可以使用这里的文档反而。但这很尴尬,因为您不能使用标准输入既向 awk 提供输入又向脚本提供输入。您需要使用额外的文件描述符(请参阅什么时候会使用额外的文件描述符? 文件描述符和 shell 脚本)。

#!/bin/bash
ls -lhF "$@" | awk -f /dev/fd/3 3<<'EOF'
( NF >= 9 ) {
    print "|-- [" $5 "] " $9
}
EOF

在 awk 中,您可以使用getline函数和管道构造从另一个命令读取输入。这不是 awk 的主要设计用途,但可以使其工作。您需要引用底层 shell 的文件名参数,这很容易出错。由于要处理的文本不是来自预期的来源(标准输入或命令行上命名的文件),因此您最终会得到块中的所有代码BEGIN

#!/usr/bin/awk -f
BEGIN {
    command = "ls -lhF"
    for (i = 1; i <= ARGC; i++) {
        arg = ARGV[i];
        gsub("'", "'\\''", arg);
        command = command " '" arg "'";
    }
    ARGC = 0; for (i in ARGV) delete ARGV[i];
    while ((command | getline) > 0) {
        if (NF >= 9) { print "|-- [" $5 "] " $9 }
    }
}

简而言之,使用 shell 来做它擅长的事情(例如将命令连接在一起),使用 awk 做它擅长的事情(例如文本处理)。

答案2

我不太确定你想要做什么,但可能出现的一个问题是awk打印出ls被认为是最后一个字段的内容,但awk不认为是这样的(通过其默认解析)。例如。

-rw-r--r-- | 433k | filename-with-no-spaces      
-rw-r--r-- |   1k | link containing  spaces -> /home/user/filename-with-no-spaces

您需要以某种方式隔离最后一个ls字段。下面采取的方法是查找所有前面的字段和分隔符的长度。其余的是文件名字段(加上其他信息,例如链接的目标)。 

下面的脚本确定可变宽度的最大宽度尺寸字段(输出格式所需)。有多种方法可以获取这个宽度;例如。(1)用于awk处理ls 输出的每一行,在主循环中,将每一行添加到数组中以供后续END{ }处理。或者(2) 将输出写入ls临时文件,然后处理awk该文件。下面所示的方法使用(2)

请注意, 的输出ls可以按照您的方式发送一些可能意外的、不简单的输出,就像 a 的情况一样link,因此使用find和自定义它的输出以更好地满足您的解析需求通常更安全。

f=7               # the number of (multi-space) delimiters before the start of the filename  
myls="$(mktemp)"  # a temp file to hold  output from `ls`
w=$(ls --color=always -lFHk ~/ |tee "$myls" |awk '{print $5}' |wc -L) # max width of size field
h=k               # size unit
awk --re-interval -v"f=$f" -v"w=$w" -v"h=$h" '
  NF >= f {
    regex = "^([^ ]+ +){"f"}" 
    match( $0, regex )  # find start of name field
    printf( "%s | %"w"s%s | %s\n", $1, $5, h, substr( $0, RLENGTH ))
  }' "$myls"
rm "$myls"

答案3

我建议避免重新发明轮子,而是使用tree,它显示目录的文件/文件夹和子目录文件/文件夹:

树(1) - Linux 手册页

姓名

树 - 以树状格式列出目录内容。

概要

树 [-adfghilnopqrstuvxACDFNS] [-L 级别 [-R]] [-H baseHREF] [-T 标题] [-o 文件名] [--nolinks] [-P 模式] [-I 模式] [--inodes] [ --device] [--noreport] [--dirsfirst] [--version] [--help] [--filelimit #] [目录 ...]

描述

Tree 是一个递归目录列表程序,可生成深度缩进的文件列表。如果设置了 LS_COLORS 环境变量,输出到 tty,并且使用了 -C 标志,则支持颜色 ala dircolors。如果没有参数,tree 会列出当前目录中的文件。当给出目录参数时,树依次列出在给定目录中找到的所有文件和/或目录。完成列出找到的所有文件/目录后,树将返回列出的文件和/或目录的总数。

默认情况下,当遇到符号链接时,符号链接引用的路径会打印在链接名称后面,格式如下:

名称 -> 真实路径

如果给出了“-l”选项并且符号链接引用实际目录,则树将遵循符号链接的路径,就好像它是真实目录一样。

相关内容