![如何按列对齐 shell“for”循环中生成的输出](https://linux22.com/image/193570/%E5%A6%82%E4%BD%95%E6%8C%89%E5%88%97%E5%AF%B9%E9%BD%90%20shell%E2%80%9Cfor%E2%80%9D%E5%BE%AA%E7%8E%AF%E4%B8%AD%E7%94%9F%E6%88%90%E7%9A%84%E8%BE%93%E5%87%BA.png)
我正在尝试对齐 bashfor
循环的输出。
目前,我从循环中获得的输出如下所示:
Directory: /some/long/directory/path Remote: some-remote
Directory: /some/dir/path Remote: other-remote
我正在尝试像这样对齐:
Directory: /some/long/directory/path Remote: some-remote
Directory: /some/dir/path Remote: other-remote
生成此输出的当前基本循环如下所示:
for dir in $(find /some/path -type d -name .git); do
cd $dir
remote=$(git remote)
printf "Directory: $dir\tRemote: $remote\n
done
我尝试过使用:
column
(它单独格式化每一行,因为它是一个 for 循环)printf
(printf "Directory: %s Remote: %s\n" "$dir" "$remote"
)awk
(echo "Directory: $dir Remote: $remote" | awk '{printf ("%s-20s %s-20s %s-20s %s-20s",$1 $2 $3 $4)}'
)
这些命令还有许多其他变体。
我可能错过了一些基本的东西(我尽力查看在线其他示例并阅读页面man
),但我无法让它工作。
如果有人指出我做错了什么,我真的很感激。
答案1
column
应该可以正常工作。但是,您不会将其添加到每个循环迭代中,但最后:
for
...
done | column -t
输出:
Directory: /some/long/directory/path Remote: some-remote
Directory: /some/dir/path Remote: other-remote
关于您的脚本的一些附加说明:
- 不要
find
像这样循环输出。检查这里。 - 引用文件/目录变量 -->
cd "$dir"
- 不要在
printf
FORMAT
字符串中使用变量。 -->printf 'Directory: %s\tRemote: %s\n' "$dir" "$remote"
答案2
你可以使用column
,但是
- 你必须喂它全面的循环输出而不是每次迭代输出,并且
- 你应该避免迭代的输出
find
。
由于您指示您的 shell 是bash
,因此您可以使用globstar
and nullglob
(以及,正如@Kusalananda 所指出的dotglob
)选项直接从 shell 进行迭代:
shopt -s globstar
shopt -s nullglob
shopt -s dotglob
for d in **/.git/
do
cd "$d"
printf "Directory: %s\tRemote: %s\n" "${d%/.git/}" "$(git remote)"
cd - >/dev/null
done | column -t -s $'\t'
- 该
globstar
选项启用**
与任何深度的中间目录匹配的 glob(又名“通配符”),从而允许您在循环内深入到目录树。 nullglob
如果没有匹配的目录,该选项将确保您根本不会进行迭代。如果没有它,glob 模式将被视为引用带有以下内容的目录字面名称**/.git/
如果没有文件名匹配,则循环将使用这个(无意义的)值执行一次$d
。- 该
dotglob
选项将确保**
也匹配“隐藏”中间目录(即以 a 开头的目录.
,如 中some/directory/.path/repository
)。 - 由于您不需要
.git/
路径的 - 部分,因此 shell 字符串处理指令将从 的值中${d%/.git/}
删除最后一次出现的。/.git/
$d
- 循环内的输出是制表符分隔的,因此
column
指示该命令-s $'\t'
采用制表符作为输入列分隔符,而不是默认值(即空格分隔的列)。
答案3
重写你的循环:
find /some/path -type d -name .git -exec sh -c '
for dirpath do
printf "Directory: %s@Remote: %s\n" \
"${dirpath%/.git}" \
"$( git -C "$dirpath" remote )"
done' sh {} + |
column -s '@' -t
这用于column
格式化 的输出find
。该find
命令查找所有.git
目录并在内联的帮助下输出所需的信息sh -c
脚本输出所需的信息,该脚本是通过批量找到的目录路径调用的。对于输出,内联脚本将给定的目录路径修改为不是在末尾包含实际内容/.git
,这意味着它们将指向 Git 项目目录。
输出用@
字符制成表格,column
稍后用于对齐数据。如果需要使用其他字符,请更改@
内联sh -c
脚本和调用中的。column
有关的:
如果您有 Git 存储库多种的遥控器,您可能需要分别复制每个遥控器的输出:
find /some/path -type d -name .git -exec sh -c '
for dirpath do
git -C "$dirpath" remote |
while IFS= read -r remote; do
printf "Directory: %s@Remote: %s\n" \
"${dirpath%/.git}" "$remote"
done
done' sh {} + |
column -s '@' -t
在这里,我们只是从 中读取行git -C "$dirpath" remote
。每行将包含 Git 远程的名称,对于每个读取的远程,我们都会进行输出。
这可能会输出
Directory: /some/path/src Remote: origin
Directory: /some/path/src Remote: private
如果存储库/some/path/src
有两个遥控器。
只是为了好玩,我们想要 JSON 输出:
find /some/path -type d -name .git -exec sh -c '
for dirpath do
git -C "$dirpath" remote |
while IFS= read -r remote; do
jo directory="${dirpath%/.git}" remote="$remote"
done
done' sh {} +
可能的输出:
{"directory":"/some/path/src","remote":"origin"}
{"directory":"/some/path/src","remote":"private"}
{"directory":"/some/path/yash-shell","remote":"origin"}
{"directory":"/some/path/zsh-shell","remote":"origin"}
{"directory":"/some/path/datamash","remote":"origin"}