我目前正在使用
watch head -n 17 *
它有效,但也显示了 17 号之前的所有线路。基本上,我只想显示用我当前的方法显示的每个文件的最后一行。我怎样才能做到这一点?
例子
为了举例,让我们减少行数。到 7. 所以:
示例文件:
1
2
3
4
5
6
7
8
这一行:
watch head -n 7 *
输出
1
2
3
4
5
6
7
我想要的地方:
7
答案1
使用 GNU awk
:
watch -x gawk '
FNR == 17 {nextfile}
ENDFILE {if (FNR) printf "%15s[%02d] %s\n", FILENAME, FNR, $0}' ./*
其输出如下:
./file1[17] line17
./short-file2[05] line 5 is the last
请注意, glob 仅在调用./*
时展开一次。watch
您的watch head -n 17 *
漏洞是一个任意命令注入漏洞,因为它的扩展*
实际上被 shell 解释为 shell 代码,watch
调用解释其参数与空格的串联。
如果当前目录中有一个文件被调用$(reboot)
,它将重新启动。
使用-x
,我们告诉watch
跳过 shell 并直接执行命令。或者,你可以这样做:
watch 'exec gawk '\''
FNR == 17 {nextfile}
ENDFILE {if (FNR) printf "%15s[%02d] %s\n", FILENAME, FNR, $0}'\'' ./*'
用于watch
运行一个 shell,该 shell 将./*
在每次迭代时扩展该 glob。watch foo bar
实际上与 相同watch -x sh -c 'foo bar'
。使用时watch -x
,您可以指定所需的 shell,例如选择一个更强大的 shell,zsh
可以执行递归通配并限制为常规文件:
watch -x zsh -c 'awk '\''...'\'' ./**/*(.)'
如果没有gawk
,您仍然可以执行以下操作:
watch '
for file in ./*; do
[ -s "$file" ] || continue
printf "%s: " "$file"
head -n 17 < "$file" | tail -n 1
done'
给出如下输出:
./file1: line17
./short-file2: line 5 is the last
但这会降低效率,因为它意味着每个文件运行多个命令。
答案2
在这两个例子中结合head
并喜欢:tail
$ seq 1 80 | head -n 17 | tail -n 1
17
$ seq 1 10 | head -n 17 | tail -n 1
10
因此,为了解决您的实际问题,命令是:
watch 'for f in *; do head -n 17 -- "$f" 2>/dev/null | tail -n 1 ; done'
请注意该2>/dev/null
部分,它是必需的,因为 * 将匹配目录和您可能无权读取的任何文件,这将产生您可能想要隐藏的错误消息。
答案3
另一种使用 find、exec 和 sed 的方法
watch find . -type f -name \"*\" -exec sh -c \'\( printf '%-50s ' {} \; sed -n 17p {}\)\' \\\;