무슨 일이 있었나
어제 나는 린트를 돌린 뒤 '깨끗하다'고 보고했다. 근거는 명령이 아무것도 출력하지 않았다는 것뿐이었다. 종료 코드는 보지 않았다. 출력이 없다는 것은 검사기가 아무 말도 하지 않았다는 뜻이지, 통과했다고 말했다는 뜻이 아니다.
조용해지는 세 가지 경우
첫째, 출력을 버린 경우다. 로그를 줄이려고 >/dev/null 2>&1을 붙이면 실패도 조용해진다. 둘째, 파이프 뒤의 명령이 결과를 덮는 경우다. 셸에서 파이프라인의 종료 코드는 기본적으로 마지막 명령의 것이다. 셋째, 검사기가 검사할 파일을 하나도 찾지 못한 경우다. 경로가 틀렸거나 설정에서 무시된 경로라면, 검사기는 아무 문제도 찾지 못한다.
연습용 폴더에서 확인했다
Biome 2.5.14와 bash 5.1로 확인했다. bad.ts에는 오류 두 개와 경고 하나가 나오도록 코드를 넣었다. biome lint bad.ts는 종료 코드 1을 냈다. 진단 59줄은 표준 오류로, 'Found 2 errors.' 같은 요약 3줄은 표준 출력으로 나왔다.
biome lint bad.ts >/dev/null 2>&1은 아무것도 출력하지 않았지만 종료 코드는 그대로 1이었다. 화면만 보면 통과와 구별되지 않는다.
biome lint bad.ts 2>/dev/null | tail -n 1은 'Found 1 warning.' 한 줄만 보여 주었고, 파이프라인 전체의 종료 코드는 0이었다. ${PIPESTATUS[*]}는 1 0이었다. Biome은 실패했지만, 마지막 명령인 tail이 성공했기 때문에 전체가 성공으로 보였다. 남은 한 줄도 경고 하나뿐이라는 인상을 준다.
빈 폴더를 넘기면 'Checked 0 files'와 함께 'No files were processed' 오류가 나왔고 종료 코드는 1이었다. 이 버전은 0개 검사를 실패로 처리하지만, 모든 도구가 그렇지는 않다. 그래서 요약에 나온 파일 수도 함께 본다.
판단은 종료 코드로 한다
검사 명령 바로 뒤에서 종료 코드를 읽는다.
biome lint src; echo "exit=$?"
파이프를 써야 한다면 set -o pipefail을 켠다. 같은 biome lint bad.ts 2>/dev/null | tail -n 1이 이번에는 종료 코드 1을 냈다. 스크립트 전체에 켜 두기 어렵다면, 파이프라인 바로 다음 줄에서 ${PIPESTATUS[0]}로 첫 명령의 종료 코드를 읽는다. 이 값은 다음 명령이 실행되면 바뀌므로 바로 읽어야 한다.
보고할 때
'깨끗하다'고 쓰기 전에 무엇을 실행했고, 종료 코드가 무엇이었고, 몇 개의 파일을 검사했는지를 같이 적는다. 종료 코드를 확인하지 않았다면 '실행했지만 결과는 확인하지 못했다'고 쓴다.
요약
출력이 없다는 것은 통과가 아니다. 종료 코드를 보고, 파이프에는 pipefail을 켜고, 검사한 파일 수가 0이 아닌지 확인한 다음에 통과라고 말한다.
What happened
Yesterday I ran a linter and reported it clean. My only evidence was that the command printed nothing. I didn't look at the exit code. No output means the checker said nothing, not that it said the code passed.
Three ways a check goes quiet
One: you threw the output away. Adding >/dev/null 2>&1 to cut log noise silences failures too. Two: a later command in a pipe covers the result. By default, a shell pipeline's exit code is the last command's. Three: the checker found nothing to check. If the path is wrong or ignored by the config, the checker finds no problems because it never looked.
Checked in a scratch folder
I checked this with Biome 2.5.14 and bash 5.1. bad.ts contained code that produces two errors and one warning. biome lint bad.ts exited with 1. The 59 lines of diagnostics went to stderr, and a 3-line summary such as "Found 2 errors." went to stdout.
biome lint bad.ts >/dev/null 2>&1 printed nothing, but the exit code was still 1. On screen it is indistinguishable from a pass.
biome lint bad.ts 2>/dev/null | tail -n 1 showed a single line, "Found 1 warning.", and the pipeline as a whole exited 0. ${PIPESTATUS[*]} was 1 0. Biome failed, but the last command, tail, succeeded, so the whole thing looked like a success. The one remaining line even suggests there was only a warning.
Given an empty folder, Biome printed "Checked 0 files" and a "No files were processed" error, and exited 1. This version treats zero files as a failure, but not every tool does, so also read the file count in the summary.
Judge by the exit code
Read the exit code right after the check command.
biome lint src; echo "exit=$?"
If you need a pipe, turn on set -o pipefail. With it on, the same biome lint bad.ts 2>/dev/null | tail -n 1 exited 1. If you can't enable it for the whole script, read the first command's exit code from ${PIPESTATUS[0]} on the line right after the pipeline. The next command overwrites it, so read it immediately.
When you report
Before writing "clean", write down what you ran, what the exit code was, and how many files were checked. If you didn't check the exit code, write "ran it, didn't confirm the result".
Summary
No output is not a pass. Read the exit code, turn on pipefail for pipes, check that the file count isn't zero, and only then call it a pass.
发生了什么
昨天我跑完 lint 就汇报说“干净”。唯一的依据是命令什么也没输出。我没看退出码。没有输出,意思是检查器什么都没说,而不是它说了“通过”。
检查变安静的三种情况
第一,输出被丢掉了。为了减少日志而加上 >/dev/null 2>&1,失败也会变得安静。第二,管道后面的命令盖住了结果。在 shell 里,管道的退出码默认是最后一个命令的。第三,检查器一个文件都没找到。路径写错了,或者被配置忽略了,检查器自然找不到问题,因为它根本没看。
在练习目录里验证过
我用 Biome 2.5.14 和 bash 5.1 验证过。bad.ts 里放了会产生两个错误和一个警告的代码。biome lint bad.ts 的退出码是 1。59 行诊断输出到标准错误,“Found 2 errors.” 这样的 3 行摘要输出到标准输出。
biome lint bad.ts >/dev/null 2>&1 什么也没输出,但退出码仍然是 1。只看屏幕,这和通过没有区别。
biome lint bad.ts 2>/dev/null | tail -n 1 只显示了一行 “Found 1 warning.”,整个管道的退出码是 0。${PIPESTATUS[*]} 是 1 0。Biome 失败了,但最后一个命令 tail 成功了,于是整体看起来成功。剩下的那一行甚至让人以为只有一个警告。
传入一个空目录时,Biome 输出了 “Checked 0 files” 和 “No files were processed” 错误,退出码为 1。这个版本把 0 个文件当作失败,但不是所有工具都这样,所以还要看摘要里的文件数。
用退出码来判断
在检查命令之后立刻读取退出码。
biome lint src; echo "exit=$?"
如果必须用管道,就打开 set -o pipefail。打开之后,同样的 biome lint bad.ts 2>/dev/null | tail -n 1 退出码变成了 1。如果没法对整个脚本打开,就在管道的下一行用 ${PIPESTATUS[0]} 读取第一个命令的退出码。下一条命令会覆盖它,所以要马上读。
汇报的时候
写“干净”之前,把运行了什么、退出码是多少、检查了几个文件一起写上。如果没有确认退出码,就写“已运行,结果未确认”。
小结
没有输出不等于通过。看退出码,管道打开 pipefail,确认检查的文件数不是 0,然后才说通过。
何が起きたか
昨日、私は lint を走らせて「クリーン」と報告した。根拠はコマンドが何も出力しなかったことだけで、終了コードは見ていなかった。出力がないというのは、チェッカーが何も言わなかったということで、「通った」と言ったということではない。
チェックが静かになる三つの場合
一つ目は、出力を捨てた場合だ。ログを減らそうと >/dev/null 2>&1 を付けると、失敗も静かになる。二つ目は、パイプの後ろのコマンドが結果を覆う場合だ。シェルのパイプラインの終了コードは、既定では最後のコマンドのものになる。三つ目は、チェッカーが検査するファイルを一つも見つけなかった場合だ。パスが間違っていたり設定で無視されていたりすれば、そもそも見ていないので問題は見つからない。
練習用フォルダで確かめた
Biome 2.5.14 と bash 5.1 で確かめた。bad.ts には、エラー二つと警告一つが出るコードを入れた。biome lint bad.ts は終了コード 1 を返した。診断 59 行は標準エラーに、「Found 2 errors.」のような要約 3 行は標準出力に出た。
biome lint bad.ts >/dev/null 2>&1 は何も出力しなかったが、終了コードは 1 のままだった。画面だけ見れば、通ったときと見分けがつかない。
biome lint bad.ts 2>/dev/null | tail -n 1 は「Found 1 warning.」の一行だけを表示し、パイプライン全体の終了コードは 0 だった。${PIPESTATUS[*]} は 1 0 だった。Biome は失敗したが、最後のコマンドの tail が成功したので、全体が成功に見えた。残った一行は、警告が一つあっただけのような印象まで与える。
空のフォルダを渡すと、Biome は「Checked 0 files」と「No files were processed」のエラーを出し、終了コード 1 を返した。このバージョンは 0 ファイルを失敗として扱うが、すべてのツールがそうではない。だから要約のファイル数も一緒に見る。
判断は終了コードで
チェックのコマンドの直後に終了コードを読む。
biome lint src; echo "exit=$?"
パイプが必要なら set -o pipefail を有効にする。有効にすると、同じ biome lint bad.ts 2>/dev/null | tail -n 1 は終了コード 1 を返した。スクリプト全体で有効にできないなら、パイプラインの次の行で ${PIPESTATUS[0]} から最初のコマンドの終了コードを読む。次のコマンドで上書きされるので、すぐに読む。
報告するとき
「クリーン」と書く前に、何を実行し、終了コードが何で、何ファイルを検査したかを一緒に書く。終了コードを確かめていないなら、「実行したが結果は未確認」と書く。
まとめ
出力がないことは合格ではない。終了コードを見て、パイプには pipefail を有効にし、検査したファイル数が 0 でないことを確かめてから、合格と言う。