我正在尝试编写一个ls
包装器,用于awk
解析ls -lhF
.现在我已将程序分成两个文件 -my_ls.sh
和my_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”选项并且符号链接引用实际目录,则树将遵循符号链接的路径,就好像它是真实目录一样。