问题
我希望能够:
- 连接目录中的所有文件(常规和隐藏),
- 但我还想在每个串联的开头显示每个文件的标题。
我在网上找到了一些解决方案,我可以用tail -n +1 * 2>/dev/null
超级巧妙的技巧做#2,但它不包括隐藏文件,就像我要做的那样:cat * .* 2>/dev/null
或者甚至head * .* 2>/dev/null
cat 命令可以解决问题,但不包含文件名,并且 head 命令不会打印/连接每个文件的全部内容。
问题
有没有办法做我需要做的事情tail
,如果不是,什么是达到相同结果/输出的良好替代品。
更新一个例子
尝试连接所有文件(常规文件和隐藏文件)时的 tail 命令
[kevin@PC-Fedora tmp]$ ls -la
total 8
drwx------ 2 user user 4096 Jun 23 09:24 .
drwxr-xr-x. 54 user user 4096 Jun 23 08:21 ..
-rw-rw-r-- 1 user user 0 Jun 23 09:24 .f1
-rw-rw-r-- 1 user user 0 Jun 23 09:24 f1
-rw-rw-r-- 1 user user 0 Jun 23 09:24 .f2
-rw-rw-r-- 1 user user 0 Jun 23 09:24 f2
-rw-rw-r-- 1 user user 0 Jun 23 09:24 .f3
-rw-rw-r-- 1 user user 0 Jun 23 09:24 f3
-rw-rw-r-- 1 user user 0 Jun 23 09:24 .f4
-rw-rw-r-- 1 user user 0 Jun 23 09:24 f4
-rw-rw-r-- 1 user user 0 Jun 23 09:24 f5
[user@PC-Fedora tmp]$ tail -n +1 *
==> f1 <==
==> f2 <==
==> f3 <==
==> f4 <==
==> f5 <==
[user@PC-Fedora tmp]$ tail -n +1 * .*
==> f1 <==
==> f2 <==
==> f3 <==
==> f4 <==
==> f5 <==
==> . <==
tail: error reading '.': Is a directory
[user@PC-Fedora tmp]$
答案1
使用zsh
GNU tail
(并非所有 tail 实现都可以采用多个文件名参数,并且并非所有这样做都会显示文件名):
() { (($# == 0)) || tail -vn +1 -- "$@" < /dev/null; } *(ND)
-v
是仍然打印文件名,即使只有一个文件,D
对于 dotglob,N
对于 nullglob,使用一个匿名函数,该函数传递该 glob 的扩展并检查当前目录是否为空的特殊情况。
</dev/null
是为了部分缓解这样的事实:GNUtail
在传递名为 的文件名时-
,将改为读取 stdin。在这里,我们只是阻止它读取 stdin,但它仍然不会读取名为-
.另一种方法是使用代替 来"${@/#%-/./-}"
替换"$@"
,但这也意味着您会看到而不是for文件(仍然可能比 更好)。-
./-
==> ./- <==
==> - <==
-
==> standard input <==
您可能还需要添加.
(或-.
) glob 限定符来限制常规的仅当当前目录 ( ) 中存在目录或其他类型的非常规文件时,才可以避免出现错误或更糟糕的情况*(ND-.)
。
ksh93
与GNU相同tail
:
(
FIGNORE='@(.|..)'
set -- ~(N)*
(($# == 0)) || tail -vn +1 -- "$@" < /dev/null
)
bash
与GNU相同tail
:
(
setopt -s nullglob dotglob
set -- *
(($# == 0)) || tail -vn +1 -- "$@" < /dev/null
)
或者使用 GNUtail
和任何 POSIX sh
(包括zsh
,但仅在sh
仿真中),也限制为常规文件或符号链接到常规文件并替换-
为./-
,但可能以不同的顺序处理文件,因为我们.*
在其他文件之前处理文件:
(
set --
for file in .* *
[ -f "$file" ] || continue
[ "$file" = - ] && file=./-
set -- "$@" "$file"
done
[ "$#" -eq 0 ] || exec tail -n +1 -- "$@"
)
或者你可以使用 GNU awk
(这里使用zsh
):
() {
(( $# == 0 )) ||
gawk 'BEGINFILE{
print sep "==> "substr(FILENAME, 3)" <=="
sep = "\n"
}
{print}' "$@"
} *(ND-.)
awk
与tail
包含.-
我们通过添加在显示时删除的前缀来=
解决这两个问题。./
请注意,如果文件末尾缺少换行符,awk 会添加一个换行符。
或者使用循环:
sep=
for f (*(ND-.)) {
print -r "$sep==> $f <=="
sep=$'\n'
cat < $f
}
(cat
与 存在同样的问题-
,我们通过在 stdin 而不是参数上传递文件来解决这个问题)